Priorität von Operatoren

Gast #3280134
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
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
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
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
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
>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.
Gast
Benjamin schrieb:
> Das erste wird bei mir aber doch nicht verworfen, sondern er
> dereferenziert den zeiger doch, nachdem er inkrementiert wurde?

Nein. Es ist das Postinkrement.
Es wird erst dereferenziert und dann wird der Zeiger inkrementiert. (der 
Zeiger, nicht der Wert)
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
Ich bezog mich auf das
1
*tst_ptr++; // first increment address, then dereference it
Der Code ist Postinkrement.
Im Kommentar steht aber die Beschreibung zum Präinkrement.
Gast
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
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

Eine eigene Frage? Dieser Thread ist über 13 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.