Priorität von Operatoren

Gast #3280134
Lesenswert?

Ich habe mal folgenden Code-Schnipsel zur Priorität von Operatoren:
1
unsigned char tst_int2 = 7;
2
unsigned char tst_int = 4;
3
unsigned char *tst_ptr = &tst_int;
4

5
printf("address: %p\tvalue: %d\n", tst_ptr, *tst_ptr);
6
*tst_ptr++; // first increment address, then dereference it
7
printf("address: %p\tvalue: %d\n", tst_ptr, *tst_ptr);

ergibt folgende Ausgabe:

address: 0022FF46       value: 4
address: 0022FF47       value: 7

Hier wird zuerst die Adresse inkrementiert und danach erst referenziert.
1
printf("%X\n", tst_int);
2
tst_int = ~tst_int++; //first invert value, then increment it
3
printf("%X\n", tst_int);

ergibt folgende Ausgabe:

4
FC

Also wird zuerst invertiert und danach inkrementiert.

Wenn ich mir die Tabelle der Prioritäten auf 
http://openbook.galileocomputing.de/c_von_a_bis_z/029_c_anhang_a_001.htm#mjec9bf1c5e3fe2e46047c2f9c7aa9c531 
anschaue, verstehe ich gar nichts mehr. Wie soll das denn 
zusammenpassen? Wieso wird im ersten Fall vorher inkrementiert, im 
2.Fall aber erst hinterher?
Moderator Persönliche Seite #3280166
Lesenswert?

Benjamin schrieb:

> *tst_ptr++; // first increment address, then dereference it

Mit ausreichenden Warnungs-Einstellungen sollte dir der Compiler
hier sagen, dass dieser Stern nutzlos ist: das Ergebnis des
Dereferenzierens des Zeigers wird verworfen.

> tst_int = ~tst_int++; //first invert value, then increment it

Undefined behaviour.  (Standard § 6.5, Absatz 2)
Gast #3280188
Lesenswert?

Vielen Dank für Deine Antwort. Damit hätte ich jetzt nicht gerechnet, 
diese Sprache macht mich noch fertig...

Mit -Wall habe ich die Warnung für Nummer 2 jetzt bekommen.

Das erste wird bei mir aber doch nicht verworfen, sondern er 
dereferenziert den zeiger doch, nachdem er inkrementiert wurde?
Moderator Persönliche Seite #3280200
Lesenswert?

1
$ cc -O -Wall -Wextra -c foo.c
2
foo.c: In Funktion »main«:
3
foo.c:10:1: Warnung: berechneter Wert ist unbenutzt [-Wunused-value]
4
foo.c:14:9: Warnung: Operation auf »tst_int« könnte undefiniert sein [-Wsequence-point]
5
foo.c:5:15: Warnung: Variable »tst_int2« wird nicht verwendet [-Wunused-variable]

Benjamin schrieb:
> Damit hätte ich jetzt nicht gerechnet, diese Sprache macht mich noch
> fertig...

Naja, Prä- und Postfix-Inkrement oder -Dekrement wirken immer
irgendwann vor bzw. nach der gesamten Berechnung des Ausdrucks.
Der Standard lässt aber offen, in welcher Reihenfolge der Compiler
die einzelnen Bestandteile der Zuweisungsanweisung bewertet, d. h.
sicher, dass der Zeiger (infolge des ++-Operators) auch tatsächlich
erhöht worden ist, kann man sich erst nach der Zuweisungsanweisung
sein.  Ob der Zeiger vor der Zuweisung selbst oder erst danach
inkrementiert wird, ist eben nicht definiert.

Es ist daher grundsätzlich eine schlechte Idee, irgendetwas, das auf
der rechten Seite einer Zuweisung einen Prä- oder Postfix-Operator
hat, ebenfalls nochmal auf der linke Seite der Zuweisung stehen zu
haben.
Moderator Persönliche Seite #3280222
Lesenswert?

Benjamin schrieb:
> Das erste wird bei mir aber doch nicht verworfen, sondern er
> dereferenziert den zeiger doch, nachdem er inkrementiert wurde?

Oliver S. schrieb:
> Mit dem dereferenzierten Wert passiert dann aber nichts.

Es gibt übrigens einen Fall, bei dem man sowas trotzdem zuweilen
gebrauchen kann: wenn der Zeiger auf ein Objekt zeigt, welches als
“volatile” markiert ist.  Das kann dann beispielsweise ein IO-Register
eines Controllers sein, und bei dem ist es schon wichtig, dass es
auch wirklich gelesen wird, denn das Lesen kann ja in der Hardware
Seiteneffekte haben (bspw. das Rücksetzen einer Interruptbedingung).

Wenn man sowas wirklich braucht, dann sollte man das aber auch
explizit hinschreiben:
1
  (void)*udr_pointer;  // read UDR to clear any pending interrupt

Der Typecast nach void macht dabei klar, dass es die volle Absicht
des Programmierers war, den Wert zu verwerfen.
Gast #3280256
Lesenswert?

>Es ist daher grundsätzlich eine schlechte Idee, irgendetwas, das auf
>der rechten Seite einer Zuweisung einen Prä- oder Postfix-Operator
>hat, ebenfalls nochmal auf der linke Seite der Zuweisung stehen zu
>haben.

Stimmt, das kommt mir wieder bekannt vor. Nochmal danke für die 
ausführliche Antwort.
#3280381
Lesenswert?

DirkB schrieb:
> Es wird erst dereferenziert und dann wird der Zeiger inkrementiert. (der
> Zeiger, nicht der Wert)

Du kannst nur sicher sein, daß der nicht-inkrementierte Zeiger 
dereferenziert wird. Genaue Ausführung und Reihenfolge von 
Inkremetierung und Dereferenzierung sind nicht definiert. Genau deshalb 
ist
>i = i++;
undefined behaviour.

Oliver
Gast #3288153
Lesenswert?

Jörg Wunsch schrieb:
> Wenn man sowas wirklich braucht, dann sollte man das aber auch
> explizit hinschreiben:
>   (void)*udr_pointer;  // read UDR to clear any pending interrupt
>
> Der Typecast nach void macht dabei klar, dass es die volle Absicht
> des Programmierers war, den Wert zu verwerfen.

Hmm, in meinem Fall würde es eher davon ablenken. Ich würde mich halt 
fragen, wozu dieser unsinnige Cast da sein soll, statt warum das 
Ergebnis der Dereferenzierung verworfen wird. ;-)
Gast #3288184
Lesenswert?

Benjamin schrieb:
> unsigned char tst_int2 = 7;
> unsigned char tst_int = 4;
> unsigned char *tst_ptr = &tst_int;
>
> printf("address: %p\tvalue: %d\n", tst_ptr, *tst_ptr);
> *tst_ptr++; // first increment address, then dereference it
> printf("address: %p\tvalue: %d\n", tst_ptr, *tst_ptr);

Die Anweisung *tst_ptr++ inkrementiert die Speicherstelle! Das die Zahl 
7 dabei rauskommt ist Glück. Ist zufällig der Inhalt von tst_int2.

Wenn Du den Inhalt von tst_int ändern möchtest, musst Du klammern: 
(*tst_ptr)++

Ergibt dann 5.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren