Auswertungsreihenfolge in C, Objective-C und C++
Problem
Die Auswertungsreihenfolge der Operanden eines beliebigen C-Operators (Objective-C oder C++) ist nicht festgelegt (sofern nicht nachstehend angegeben). Dies umfasst die Auswertungsreihenfolge von Funktionsargumenten bei einem Funktionsaufruf und die Auswertungsreihenfolge von Teilausdrücken in einem beliebigen Ausdruck. Der Compiler wertet sie in beliebiger Reihenfolge aus und wählt möglicherweise eine andere Reihenfolge, wenn derselbe Ausdruck erneut ausgewertet wird.
Vorteil: Es vereinfacht die Arbeit des Compilers und ermöglicht in bestimmten Situationen die Erzeugung von sehr effizientem Code.
Nachteil: Nebeneffekte! Sie hängen nicht nur von spezifischer Hardware, Plattform und Compiler ab, sondern auch von bestimmten Einstellungen (Optimierungsstufe, Debug- / Release-Version) desselben Compilers. Schlussendlich führt das zu einem nicht-portierbarem Code und schwer zu erkennenden Bugs.
Beispiele
• Auswertungsreihenfolge der Operanden eines beliebigen Operators (z. B. +, -, =, *, /)
Ausnahmen:
- logische Operatoren (&& und ||)
- logischer Operator (?:)
- Komma-Operator ,
Erstaunlicherweise könnte die Ausgabe jede der folgenden sein: „1 2 3“, „2 1 3“, „3 2 1“, „2 1 3“, „1 3 2“ und „3 1 2“!
Der Ausdruck f1 () + f2 () + f3 () wird aufgrund der Links-Rechts-Assoziativität des Operators + als (f1 () + f2 ()) + f3 () geparsed. Der Funktionsaufruf von f3 wird entweder zuerst, als letztes oder zwischen f1 () oder f2 () zur Laufzeit ausgewertet.
Die Reihenfolge der Auswertung von Funktionsargumenten in einem Funktionsaufruf:
Ergebnisse:
- Das Ergebnis mit Clang / PPC-Architektur
- Das Ergebnis mit Clang / i386-Architektur
Nachfolgend wir die Lösung des Problems gezeigt.
Lösung
Keinen Code schreiben, der von der Reihenfolge der Auswertung abhängt (der Nebeneffekte hat)!
- Compiler-Warnungen aktivieren und beachten
- Statische Analysetools (wie der von Clang, cppcheck usw.) nutzen, um noch mehr Warnungen zu erhalten
Weitere Aspekte
Undefiniertes Verhalten ist ein Verhalten, das bei verschiedenen Implementierungen einer Programmiersprache variieren kann. Das genaue Verhalten kann bei der Untersuchung des zugehörigen Quellcodes nicht vollständig ermittelt werden.
---
Autor:
Andreas Maier, Software Architect, Business Division Banking & Insurance