mikrocontroller.net

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


Autor: Timo P (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
unsigned char a = 1;
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!

Autor: Sebihepp (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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".

Autor: Timo P (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
unsigned char a = 1;
printf("%u %u %u %u\n",a,a++,a++,a++);

leicht überprüfen.

Autor: Klaus T. (gauchi)
Datum:

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

Autor: IchNix (Gast)
Datum:

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

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

Autor: Peter Dannegger (peda)
Datum:

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


Peter

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Wegstaben Verbuchsler (wegstabenverbuchsler)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin (Gast)
Datum:

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

Das ist systemspezifisch.

Autor: U.R. Schmitt (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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'))

Autor: Hermann-Josef M. (hermann-josef)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

siehe hier: http://www.c-faq.com/expr/comma.html
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

Autor: doch g (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Andreas B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: df1as (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Andreas B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.