C で呼び出すときに、関数パラメーターの評価順序を想定できますか?
いいえ、未指定の動作であると仮定することはできません。C99 標準の草案のセクション6.5
パラグラフに3
は次のように記載されています。
演算子とオペランドのグループ化は、構文によって示されます.74) 後で指定される場合を除き (関数呼び出し ()、&&、||、?:、およびカンマ演算子について)、部分式の評価の順序およびどちらの副作用が発生するかは、どちらも特定されていません。
それはまた、 except as specified later および 具体的には sites と述べているため、セクション関数呼び出しのパラグラフfunction-call ()
の草案標準の後半で次のように述べていることがわかります。6.5.2.2
10
関数指定子、実引数、および実引数内の部分式の評価順序は指定されていませんが、実際の呼び出しの前にシーケンス ポイントがあります。
このプログラムは、シーケンス ポイント間で複数回変更しているため、未定義の動作も示します。ドラフト標準セクションの段落から:pa
6.5
2
前のシーケンス ポイントと次のシーケンス ポイントの間で、オブジェクトの格納値は、式の評価によって最大 1 回変更されます。さらに、以前の値は、保存する値を決定するためにのみ読み取られます。
次のコード例は未定義として引用されています。
i = ++i + 1;
a[i++] = i;
コンマ演算子はシーケンス ポイントを導入しますが、関数呼び出しで使用されるコンマはセパレータであり、comma operator
. セクション6.5.17
Comma operatorの段落を見ると、次のように書かれ2
ています。
コンマ演算子の左側のオペランドは void 式として評価されます。その評価の後にシーケンス ポイントがあります。
しかし、段落3
は言う:
例 構文で示されているように、リスト内の項目を区切るためにコンマが使用されているコンテキスト (関数への引数や初期化子のリストなど) では、コンマ演算子 (この節で説明されている)を使用することはできません。
gcc
これを知らずに、少なくとも使用して警告をオンにすると-Wall
、次のようなメッセージが表示されます。
warning: operation on 'pa' may be undefined [-Wsequence-point]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
^
デフォルトclang
では、次のようなメッセージで警告します。
warning: unsequenced modification and access to 'pa' [-Wunsequenced]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
~ ^
一般に、最も効果的な方法でツールを使用する方法を理解することが重要です。警告に使用できるフラグを知ることは重要です。その情報はここでgcc
見つけることができます。便利で、長期的には多くのトラブルを回避できるいくつかのフラグは、 と の両方に共通です。理解するには-fsanitizeが非常に役立ちます。たとえば、実行時に未定義の動作の多くのインスタンスをキャッチします。gcc
clang
-Wextra -Wconversion -pedantic
clang
-fsanitize=undefined