ProgrammingシニアC開発者、システムプログラマ

C言語では関数の引数を計算する際に副作用がどのように実現されますか?引数の計算順序はどのようになっていて、どのような予期しない問題が発生する可能性がありますか?

Hintsage AIアシスタントで面接を突破

回答

C言語では関数の引数の計算順序は標準で定義されていません(C99まで)。引数は左から右、右から左、またはコンパイラやアーキテクチャの裁量で任意の順序で計算される可能性があります。

  • 引数内のすべての式/副作用は、関数呼び出しの前に完了する必要がありますが、特定の順序で実行される保証はありません。
  • これは、複数の引数で値を変更する変数を使用すると、未定義の動作(undefined behavior)またはアーキテクチャ間の単純な違いの潜在的な原因になることを意味します。

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コードにありました。