C言語では関数の引数の計算順序は標準で定義されていません(C99まで)。引数は左から右、右から左、またはコンパイラやアーキテクチャの裁量で任意の順序で計算される可能性があります。
void fn(int a, int b) { /* ... */ } int x = 1; fn(x++, x++); // x++ と x++ の計算順序は未定義です!
「C言語の関数の引数はどのような順序で計算され、それに頼れるのでしょうか?」
一般的な誤解は、引数は左から右に計算されると考えることです(式と似ているため)。実際には、各関数呼び出しはコンパイラ(およびプラットフォーム)の裁量でコンパイルされます。
void foo(int a, int b, int c); int x = 1; foo(x++, x++, x++); // 結果は引数の計算順序に依存します
正しい答え:頼ることはできません — 挙動は未定義です!
物語
クロスプラットフォーム製品で、プログラマーは
push(stack, stack->size++, data);を書きました。ほとんどのプラットフォームでは問題ありませんでしたが、一つのプラットフォームではデータを渡す前にスタックのサイズが増加し、別のプラットフォームでは後になって増加しました。データは「失われた」か、不正確にアドレス指定され、エラーはまれにしか現れず、非常にデバッグが困難でした。
物語
ネットワークプロトコルライブラリでは、統計カウンタをインクリメントする式引数でロギング関数が呼び出されていました。統計レポートは不正確に生成されました:異なるクライアント間でカウンタのインデックスが異なり、期待された順序で実行されなかったためです。
物語
ハードウェア制御インターフェースで、初期化関数が引数内でポインタとオフセットをインクリメントしながら渡しました(
init(ptr++, cnt++);のように)。一部のプロセッサではハードウェアが正しく初期化されましたが、他のプロセッサではエラーが発生し、その原因はハードウェアにあると長い間探されましたが、問題は不正確なCコードにありました。