ProgrammierungLeitender C-Entwickler, Systemprogrammierer

Wie wird in der Programmiersprache C die Ausführung von Nebeneffekten bei der Berechnung der Funktionsargumente realisiert? Wie ist die Reihenfolge der Berechnung der Argumente, und welche Überraschungen können dabei auftreten?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

In der Programmiersprache C ist die Reihenfolge der Berechnung der Funktionsargumente nicht durch den Standard definiert (bis einschließlich C99). Die Argumente können von links nach rechts, von rechts nach links oder in jeder anderen Reihenfolge berechnet werden (nach Ermessen des Compilers oder der Architektur).

  • Alle Ausdrücke/Nebeneffekte in den Argumenten müssen vor dem Funktionsaufruf abgeschlossen sein, aber es gibt keine Garantie, dass sie in einer bestimmten Reihenfolge ausgeführt werden.
  • Das bedeutet, dass die Verwendung von Variablen mit Wertänderung in mehreren Argumenten eine potenzielle Quelle für undefiniertes Verhalten (undefined behavior) oder einfach Unterschiede zwischen Architekturen darstellt.

Beispiel

void fn(int a, int b) { /* ... */ } int x = 1; fn(x++, x++); // die Reihenfolge der Berechnung von x++ und x++ ist nicht definiert!

Fangfrage

"In welcher Reihenfolge werden die Funktionsargumente in C berechnet und kann man sich darauf verlassen, wenn man Code schreibt?"

Ein häufiger Fehler ist zu glauben, dass die Argumente von links nach rechts berechnet werden (analog zu Ausdrücken). In der Praxis wird jeder Funktionsaufruf nach Ermessen des Compilers (und der Plattform) kompiliert.

void foo(int a, int b, int c); int x = 1; foo(x++, x++, x++); // das Ergebnis hängt von der Reihenfolge der Berechnung der Argumente ab

Die wirkliche Antwort: man kann sich nicht darauf verlassen — das Verhalten ist nicht definiert!

Beispiele für reale Fehler aufgrund mangelnden Wissens über die Feinheiten des Themas


Geschichte

In einem plattformübergreifenden Produkt schrieb ein Programmierer push(stack, stack->size++, data);. Auf den meisten Plattformen funktionierte alles, aber auf einer erhöhte sich die Stapelgröße vor der Datenübergabe, auf einer anderen danach. Daten wurden "verloren" oder falsch adressiert, der Fehler trat selten auf und war sehr schwierig zu debuggen.


Geschichte

In der Netzwerkprotokollbibliothek wurde die Protokollierungsfunktion mit Argumentausdrücken aufgerufen, die statistische Zähler erhöhten. Statistische Berichte wurden falsch generiert: Bei verschiedenen Kunden wichen die Zählerindizes ab, da sie nicht in der erwarteten Reihenfolge ausgeführt wurden.


Geschichte

In der Hardwaresteuerungsschnittstelle übergab die Initialisierungsfunktion Zeiger und Offsets mit Inkrement innerhalb der Argumente (z.B. init(ptr++, cnt++);). Auf einigen Prozessoren wurde die Hardware korrekt initialisiert, auf anderen trat ein Fehler auf, die Ursache wurde lange in der Hardware gesucht, obwohl das Problem im fehlerhaften C-Code lag.