Forum: Mikrocontroller und Digitale Elektronik Was macht der compiler aus sprintf(s,"%u %u",a,a++);


von Timo P (Gast)


Lesenswert?

1
unsigned char a = 1;
2
printf("%u %u\n",a,a++);

Ausgabe ist:

2 1 anstelle von 1 2

Warum? Ich habe versucht, mir das assembler-listing anzuschauen, aber 
das hilft mir als c-programmierer nicht weiter. Meine Vermutung ist, 
dass beim Aufruf vom printf-befehl zuerst die argumente 
verarbeitet/berechnet werden.

Dann müsste ich allerdings 2 2 als Ausgabe erhalten.
Ich stehe auf dem schlauch!

Danke für eure Hilfe!

von Sebihepp (Gast)


Lesenswert?

a++ ist der Postfixoperator. Erst wird der Wert verarbeitet, dann 
inkementiert.
++a ist der Prefixoperator. Der Wert wird erst inkrementiert und dann 
verarbeitet.

Da die Elemente von rechts nach links auf den Stack gelegt werden ist 
die Ausgabe nunmal "2 1".

von Timo P (Gast)


Lesenswert?

Sebihepp schrieb:
> Da die Elemente von rechts nach links auf den Stack gelegt werden

?!? Wirklich?


ich dachte, dass wenn ich in printf formatierte ausgaben in den string 
packe "von links nach rechts" dann werden die zugehörigen Argumente auch 
"von links nach rechts" angegeben, genauso auch die Ausgabe...

lag ich da falsch? Ich denke nicht oder? Das mit dem post oder präfixop. 
kenne ich. Aber die umgekehrte Reihenfolge hab ich immer noch nicht
verstanden...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Sie werden in der Reihenfolge ausgegeben, die dort steht. Aber beim 
Aufruf der Funktion werden sie "von rechts" auf den Stack gepackt und in 
der Funktion entsprechend wieder abgeräumt.

Das kannst Du mit
1
unsigned char a = 1;
2
printf("%u %u %u %u\n",a,a++,a++,a++);

leicht überprüfen.

von Klaus T. (gauchi)


Lesenswert?

ist nicht sowieso die gleichzeitige Benutzung von a und a++ in einer 
Anweisung undefined?

von IchNix (Gast)


Lesenswert?

> Was macht der compiler aus sprintf(s,"%u %u",a,a++);

Der macht sich einen Spaß draus. Das läuft unter "undefined bahaviour".

von Peter D. (peda)


Lesenswert?

Der Kommaoperator muß den Wert des letzten Ausdrucks zurückgeben.
Aber wann er die Ausdrücke berechnet, ist undefiniert.


Peter

von Peter (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Der Kommaoperator muß den Wert des letzten Ausdrucks zurückgeben.
> Aber wann er die Ausdrücke berechnet, ist undefiniert.

sprintf(s,"%u %u",a,a++);

aber hierbei handelt es sich doch nicht um ein Kommaoperator oder etwa 
doch?

von Rolf Magnus (Gast)


Lesenswert?

Peter schrieb:
> aber hierbei handelt es sich doch nicht um ein Kommaoperator oder etwa
> doch?

Nein.

Timo P schrieb:
> ich dachte, dass wenn ich in printf formatierte ausgaben in den string
> packe "von links nach rechts" dann werden die zugehörigen Argumente
> auch "von links nach rechts" angegeben, genauso auch die Ausgabe...
>
> lag ich da falsch?

Ja. Die Reihenfolge ist in C nicht spezifiziert. Mit anderen Worten: Du 
darfst von keiner spezifischen Reihenfolge ausgehen, da das jeder 
Compiler tun kann, wie er will.

von Simon B. (nomis)


Lesenswert?

Peter Dannegger schrieb:
> Der Kommaoperator muß den Wert des letzten Ausdrucks zurückgeben.
> Aber wann er die Ausdrücke berechnet, ist undefiniert.

Das ist nicht der Kommaoperator, das Komma trennt hier schlicht nur die 
Funktionsargumente.

Die Reihenfolge, in der die Funktionsargumente ausgewertet werden (bevor 
die Funktion dann aufgerufen wird) ist meines Wissens undefiniert. Hier 
wird halt das letzte Argument ausgewertet (Postfix, d.h. Rückgabe = 1, 
dann wird a inkrementiert) und dann das vorletzte (nun ist aber a schon 
2).

viele Grüße,
       Simon

von Rolf Magnus (Gast)


Lesenswert?

Simon Budig schrieb:
> Die Reihenfolge, in der die Funktionsargumente ausgewertet werden
> (bevor die Funktion dann aufgerufen wird) ist meines Wissens
> undefiniert.

-pedantic: Nicht undefiniert, sondern unspezifiziert.

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

@Autor: Timo P (Gast)

wozu braucht man so ein Konstrukt ..

> unsigned char a = 1;
> printf("%u %u\n",a,a++);

.. außer um zu schauen, ob oder was der Compiler draus macht, bzw. 
prüfen zu lassen ob der Ausdruck syntaktisch verarbeitbar ist?

Oder hast du ernsthaft vor, solche unverständlichen (nicht sinnvol 
lesbaren) Codezeilen in irgend einem Stückchen Software zu verstecken? 
Wartbar ist sowas allemale nicht.

von Martin (Gast)


Lesenswert?

Sebihepp schrieb:
> Da die Elemente von rechts nach links auf den Stack gelegt werden ist
> die Ausgabe nunmal "2 1".

Das ist systemspezifisch.

von U.R. Schmitt (Gast)


Lesenswert?

Das war zumindest früher nicht spezifiziert.
Ich kenne einen Fall, daß durch so ein Konstrukt:
(if ((*pc++ == '0') && (*pc == '0'))
locker 120.000 DM Schaden entstanden sind. Wurde von MSC 5.0 so 
übersetzt wie gewünscht, MSC 6.0 hat dann aber leider den increment nach 
hinten gezogen. Dadurch wurde 2 mal das erste Zeichen verglichen.
Pech daß diese 2 Zeichen der Returncode der Authorisierungszentrale für 
eine Reihe von Geldautomaten waren und die dann munter ausgezahlt hat 
weil er '00' verstanden hat (auszahlen) aber tatsächlich '04' kam (Karte 
einziehen, Manipulationsverdacht).
Eine Bande hatte das spitz gekriegt und munter mit geklauten Karten die 
längst gesperrt waren Kohle abgehoben.

von (prx) A. K. (prx)


Lesenswert?

Interessant. Im Gegensatz zum Komma in Parameterlisten(!) sind die 
Operatoren && und || nämlich sequence points, d.h. das Verhalten ist in 
diesem Fall durchaus definiert und der Compiler darf es nicht ganz nach 
hinten ziehen.

Anders sähe es da aus:
  if ((*pc++ == '0') & (*pc == '0'))

von Hermann-Josef M. (hermann-josef)


Lesenswert?

Hallo,

siehe hier: http://www.c-faq.com/expr/comma.html
1
The order of evaluation of the arguments to a function call is unspecified.
Lässt man das obige Beispiel in einem C-Interpreter laufen (hier CINT), 
dann kommt "1 1" raus, also da wird es von links nach rechts 
interpretiert.

Hermann-Josef

von doch g (Gast)


Lesenswert?

U.R. Schmitt schrieb:
> Das war zumindest früher nicht spezifiziert.
> Ich kenne einen Fall, daß durch so ein Konstrukt:
> (if ((*pc++ == '0') && (*pc == '0'))
> locker 120.000 DM Schaden entstanden sind. Wurde von MSC 5.0 so
> übersetzt wie gewünscht, MSC 6.0 hat dann aber leider den increment nach
> hinten gezogen. Dadurch wurde 2 mal das erste Zeichen verglichen.
> Pech daß diese 2 Zeichen der Returncode der Authorisierungszentrale für
> eine Reihe von Geldautomaten waren und die dann munter ausgezahlt hat
> weil er '00' verstanden hat (auszahlen) aber tatsächlich '04' kam (Karte
> einziehen, Manipulationsverdacht).
> Eine Bande hatte das spitz gekriegt und munter mit geklauten Karten die
> längst gesperrt waren Kohle abgehoben.

Hast du dafür eine Quelle? Das wäre ein prima Beispiel für "sauberes 
Programmieren". Hätte man *pc==0 && *(pc+1)==0 geschrieben wäre alles 
gut gegangen.

von Andreas B. (Gast)


Lesenswert?

U.R. Schmitt schrieb:
> Ich kenne einen Fall, daß durch so ein Konstrukt:
> (if ((*pc++ == '0') && (*pc == '0'))
> locker 120.000 DM Schaden entstanden sind. Wurde von MSC 5.0 so
> übersetzt wie gewünscht, MSC 6.0 hat dann aber leider den increment nach
> hinten gezogen.

Dann war ein Bug in MSC 6.0 oder an dieser Geschichte stimmt etwas 
nicht. Ich sehe nicht, wie hier das Inkrement nach hinten (wohin?) 
gezogen werden könnte.

doch g schrieb:
> Hast du dafür eine Quelle? Das wäre ein prima Beispiel für "sauberes
> Programmieren". Hätte man *pc==0 && *(pc+1)==0 geschrieben wäre alles
> gut gegangen.

Wenn schon, dann "pc[0]==0 && pc[1]==0". Davon abgesehen rettet einen 
sauberes Programmieren auch nicht vor Bugs im Compiler.

von df1as (Gast)


Lesenswert?

Die Bindungsstärken sind zwar eindeutig bei *pc++, aber *(pc++) ist 
deutlicher - (*pc)++ soll es ja nicht sein.

Oder so:

if (*(pc++) == 0)
  if (*pc == 0)
    ...

Hier wird auch ersichtlich, dass nur die erste Anweisung unbedingt 
ausgeführt wird. Das kann in verketteter Logik m. E. genauso passieren. 
Ein weiteres ++ sollte man sich dann besser verkneifen, außer, man 
braucht pc hinterher nicht mehr.

von Andreas B. (Gast)


Lesenswert?

df1as schrieb:
> Oder so:
>
> if (*(pc++) == 0)
>   if (*pc == 0)
>     ...
>
> Hier wird auch ersichtlich, dass nur die erste Anweisung unbedingt
> ausgeführt wird. Das kann in verketteter Logik m. E. genauso passieren.

Ein Programm leserlich zu schreiben ist eine Sache, aber als "Publikum" 
sollte man diejenigen anpeilen, die von C mindestens eine grundlegende 
Ahnung haben. Auf logische Operatoren wie && und || zu verzichten nur 
weil ein C-unbefleckter die nicht verstehen könnte geht definitiv zu 
weit.

Und bei Verzicht auf diese Operatoren kommt bei allem ausser trivialen 
Bedingunen eine Wust von verschachtelten ifs raus und damit das 
Gegenteil von leserlich.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas B. schrieb:
> Ich sehe nicht, wie hier das Inkrement nach hinten (wohin?)
> gezogen werden könnte.

Da wird nichts "nach hinten gezogen".  Es ist einfach vom Standard
nicht definiert, wann das Inkrement passiert außer, dass es mit
dem Erreichen des nächsten "sequence points" vollzogen sein muss.

von Peter D. (peda)


Lesenswert?

Peter schrieb:
> Peter Dannegger schrieb:
>> Der Kommaoperator muß den Wert des letzten Ausdrucks zurückgeben.
>> Aber wann er die Ausdrücke berechnet, ist undefiniert.
>
> sprintf(s,"%u %u",a,a++);
>
> aber hierbei handelt es sich doch nicht um ein Kommaoperator oder etwa
> doch?

Stimmt, Du hast recht.
Der Kommaoperator ist ein Sequence point, d.h. es muß von links nach 
rechts gearbeitet werden.
Also ist das Komma in einer Argumentenliste kein Kommaoperator.

"Note that a function call f(a,b,c) is not a use of the comma operator 
and the order of evaluation for a, b, and c is unspecified."


Peter

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.