Forum: Compiler & IDEs Simple Pointer Frage


von David H. (davidm)


Lesenswert?

Warum wird buffer++ ohne ein * geschrieben und *data++ mit einem *
Ich verstehs einfach nicht, in beiden fällen wird der Pointer erhöht.
1
  void getsUSART(char *buffer, unsigned char len)
2
  {
3
    char i;    // Length counter
4
    unsigned char data;
5
  
6
    for(i=0;i<len;i++)  // Only retrieve len characters
7
    {
8
      while(!DataRdyUSART());// Wait for data to be received
9
  
10
      data = getcUART();    // Get a character from the USART
11
                             // and save in the string
12
      *buffer = data;
13
      buffer++;          <<<----- HIER    // Increment the string pointer
14
    }
15
  }
16
  
17
  void putsUSART( char *data)
18
  {
19
    do
20
    {  // Transmit a byte
21
      while(BusyUSART());
22
      putcUART(*data);
23
    } while( *data++ );  <<<----- HIER
24
  }

von Daniel_s (Gast)


Lesenswert?

Hi,

data = *buffer++;

ist das gleiche wie

data = *buffer;
buffer++;

Nur kürzer geschrieben

von Garden (Gast)


Lesenswert?

buffer++; // Der Pointer zeigt anschließend auf den nächsten Eintrag

*data++;  // Es wird zunätzlich nachgeschaut, welchen Wert der Inhalt 
hat, auf den der Zeiger zeigte.

Zu Pointern:

http://et-tutorials.de/3368/pointer-in-c/

von David H. (davidm)


Lesenswert?

Daniel_s schrieb:

ok, nun lassen wir mal das auslesen in die data variable weg, dann 
bleibt nur ein

buffer++;

Und warum hat dieses buffer++ die selbe funktion wie ein *data++ ?

von B. S. (bestucki)


Lesenswert?

> Und warum hat dieses buffer++ die selbe funktion wie ein *data++ ?

Weil ++ stärker bindet als *

Stichwort: Operatorrangfolge

von Yalu X. (yalu) (Moderator)


Lesenswert?

Der Ausdruck
1
buffer++

liefert als Ergebnis den Wert von buffer (also eine Adresse), und als 
Nebeneffekt wird buffer anschließend inkrementiert. Oftmals wird auch 
nur der Nebeneffekt (also die Inkrementierung) dieses Ausdrucks genutzt. 
Dann schreibt man den Ausdruck einfach isoliert in eine ANweisung.
1
*data++

liefert wegen des *-Operators als Ergebnis das Zeichen, auf das data 
zeigt und hat als Nebeneffekt ebenfalls die Inkrementierung. Wenn auch 
hier wie im ersten Beispiel nur der Nebeneffekt genutzt würde, wäre die 
Dereferenzierung unnütz, weil das Ergebnis dieser Operation ja gleich 
anschließend weggeschschmissen wird. *data++ hätte dann also den 
gleichen Effekt wie data++ (Ausnahme: data ist als volatile 
deklariert). In deinem putsUART wird der Wert von *data++ aber 
tatsächlich verwendet, nämlich um abzufragen, ob das String-Ende 
(NUL-Zeichn) erreicht ist.

von Daniel_s (Gast)


Lesenswert?

1
(*buffer)++

incrementiert die variable auf den buffer zeigt

also
1
 (*buffer)++ != *buffer++;

von David H. (davidm)


Lesenswert?

data++

und

*data++

wären nun klar.

wie sieht es aus mit:

&data++

Das dürfte doch das selbe sein wie data++

es liefert die addresse von data und inkrementiert


Und wie verhält sich das anders bei volatile?

von B. S. (bestucki)


Lesenswert?

David Mueller schrieb:
> es liefert die addresse von data und inkrementiert

Ja

Der Ausdruck
1
(&data)++
ist jedoch nicht zulässig, weil die ermittelte Adresse in keiner 
Variable gespeichert ist und somit nicht inkrementiert werden kann.

von Garden (Gast)


Lesenswert?

David,
das Problem, das Du da hast, ist wahrscheinlich folgendes:

Du hast eine globale Variable data, die als unsigned char definiert ist.

In der Funktion gibt es eine weitere Variable, die auch data heißt, die 
als

char *data

definiert ist, also ein Zeiger auf ein char.

Also, nicht durcheinander werfen.

Und besser: verschiedende Namen verwenden.

von Garden (Gast)


Lesenswert?

Sorry, die erste Variable ist nicht global, sondern in main() definiert.
Das Fazit ist aber das Gleiche: Besser unterschiedliche Namen verwenden.

von B. S. (bestucki)


Lesenswert?

Garden schrieb:
> Sorry, die erste Variable ist nicht global, sondern in main() definiert.
> Das Fazit ist aber das Gleiche: Besser unterschiedliche Namen verwenden.

Variablen in Funktionen benenne ich nach deren Inhalt, unabhängig davon, 
ob in einer anderen Funktion (dazu zählt auch main) eine Variable gleich 
heisst. Auch wenn in einer anderen Sourcedatei eine globale Veriable mit 
dem selben Namen existiert.


Folgendes sollte man jedoch zu Gunsten der Übersichtlichkeit vermeiden:
1
char data;
2
3
void foo(int data){
4
  int i;
5
  for(i=0;i<data;i++){
6
    long data;
7
    /* usw. */
8
  }
9
}

von Garden (Gast)


Lesenswert?

be stucki schrieb:
> Variablen in Funktionen benenne ich nach deren Inhalt, unabhängig davon,
> ob in einer anderen Funktion (dazu zählt auch main) eine Variable gleich
> heisst.

Du bist wahrscheinlich auch kein Programmier-Anfänger, wie der 
Frage-Steller.
Bei größeren Projekten weiß man ja auch gar nicht, wie andere ihre 
lokalen Variablen nennen. Ist auch egal.

Wenn man aber mit dem Programmieren anfängt, ist es hilfreich Variablen 
unterschiedlich zu benennen, um nicht alles durcheinader zu werfen.

von Karl H. (kbuchegg)


Lesenswert?

David Mueller schrieb:

> Und wie verhält sich das anders bei volatile?

volatile ist hier eher Nebenschauplatz. Yalu hat es nur angeführt, damit 
ihm nicht irgendwer hinten nach in die Parade fährt und 5 Postings lang 
darüber rummosert, dass er unpräzise war.

Aber um das anzusprechen. Bei
1
void foo()
2
{
3
   *data++;
4
}

kann und wird der Compiler drauf kommen, dass die Derferenzierung 
effektiv ja nichts bewirkt, weil ja mit dem so ermittelten Wert gar 
nichts gemacht wird. Der Compiler kann also die Derferenzierung effektiv 
einfach weglassen, weil sich (aus seiner Sicht) am Programm ja nichts 
ändert.
Nun, das muss aber nicht stimmen. WEnn data ein Pointer auf eine 
Hardware ist, die auf den reinen Vorgang des Auslesens hin etwas macht 
(keine Ahnung: Sirene heult, Blinklicht geht an, etc.) dann gibt es 
einen Unterschied, denn im Falle das der Compiler optimiert, findet ja 
das eigentliche Auslesen nie statt. Und mit volatile kann man das 
verhindern. volatile ist für den Compiler quasi die Aufforderung "Keine 
Optimierungen erlaubt"

von Karl H. (kbuchegg)


Lesenswert?

> Warum wird buffer++ ohne ein * geschrieben und *data++ mit einem *
> Ich verstehs einfach nicht,

Deine Frage ist fast so sinnvoll, wie die Frage:
Warum schreibt ein Prorgrammierer einmal die Berechnung des Umfangs 
eines Kreises so
1
  tmp = 2 * Radius;
2
  Umfang = tmp * PI;
und einmal so
1
  Umfang = 2 * Radius * PI;


In deinen beiden Funktionen könnte jeweils die gleiche Syntax benutzen, 
wenn man wollte. Schaun wir uns mal an, was da rauskommen würde.

Fall 1: das *data++ im puts soll beibehalten werden. Wie müsste gets 
aussehen, wenn es die gleiche Syntax benutzen will.
So
1
 void getsUSART(char *buffer, unsigned char len)
2
  {
3
    char i;    // Length counter
4
  
5
    for(i=0;i<len;i++)  // Only retrieve len characters
6
    {
7
      while(!DataRdyUSART());// Wait for data to be received
8
  
9
      *buffer++ = getcUART();    // Get a character from the USART
10
                             // and save in the string
11
    }
12
  }

warum der Programmierer das nicht gemacht hat? Keine Ahnung. Vielleicht 
hat ihn die Courage verlassen das ++ auch auf der linken Seite einer 
Zuweisung einzusetzen oder es war ihm nicht wichtig genug, weil der 
Optimizer seinen Originalcode sowieso auf diese Version zusammenkürzen 
würde. Vielleicht wollte er auch einfach nur eine Hilfsvariable haben, 
damit er mit dem Debugger rankommt.


Fall 2: Die Derferenzierung wird vom Inkrement getrennt, so wie das um 
gets gemacht ist. Wie muss dann das puts aussehen?

So
1
  
2
  void putsUSART( char *data)
3
  {
4
    char c;
5
6
    do
7
    {  // Transmit a byte
8
      while(BusyUSART());
9
10
      c = *data;
11
      data++;
12
13
      putcUART(c);
14
15
    } while( c );
16
  }
Man sieht auch: Die Einführung einer Hilfsvariablen c bringt dem Code 
eigentlich kaum Klarheit, wenn man sein C lesen kann. Sie zieht den Code 
nur unnötig in die Länge.

D.h. Ob man die eine Schreibweise benutzt oder die andere ist eigentlich 
... in diesen Fällen .... relativ egal. Das eine lässt sich in das 
andere umformen und welche Schreibweise man benutzt ist weitgehend 
Geschmackssache.

von David H. (davidm)


Lesenswert?

vielen Dank für die Ausführliche Erklärung.

Karl Heinz Buchegger schrieb:
> void foo()
> {
>    *data++;
> }


Er würde bei einer Optimierung aber nur die Derferenzierung entfernen 
oder?
Das inkrementieren wird bei behalten?

von Karl H. (kbuchegg)


Lesenswert?

David Mueller schrieb:
> vielen Dank für die Ausführliche Erklärung.
>
> Karl Heinz Buchegger schrieb:
>> void foo()
>> {
>>    *data++;
>> }
>
>
> Er würde bei einer Optimierung aber nur die Derferenzierung entfernen
> oder?
> Das inkrementieren wird bei behalten?

Kann man so nicht sagen.

ein vollständiges Programm
1
unsigned char i = 5;
2
static unsigned char * data = &i;
3
4
static void foo()
5
{
6
  *data++;
7
}
8
9
int main()
10
{
11
  foo();
12
}

Optimierungen laufen nach der 'AS-IF' Regel.
D.h. der Optimizer darf alles machen, solange der nach aussen 
beobachtbare Effekt derselbe bleibt.
Im obigen Programm kann der Compiler (nach dem inlinen) auch das 
Inkrementieren wegoptimieren, weil data sonst nirgends benutzt wird, es 
also keinerlei beobachtbare Effekte gibt ob es stattfindet oder nicht.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.