未定義の動作とシーケンスポイントについて この回答 を読んだ後、小さなプログラムを作成しました。
#include <stdio.h>
int main(void) {
int i = 5;
i = (i, ++i, 1) + 1;
printf("%d\n", i);
return 0;
}
出力は2
です。ああ、神様、私は減少が来るのを見なかった!ここで何が起きてるの?
また、上記のコードをコンパイルしているときに、次のような警告が表示されました。
px.c:5:8:警告:コンマ式の左側のオペランドは効果がありません
[-Wunused-value] i = (i, ++i, 1) + 1; ^
どうして?しかし、おそらく私の最初の質問の答えによって自動的に答えられるでしょう。
式(i, ++i, 1)
では、使用されるコンマは コンマ演算子 です。
カンマ演算子(トークン
,
で表される)は、最初のオペランドを評価して結果を破棄し、2番目のオペランドを評価してこの値(および型)を返すバイナリ演算子です。
最初のオペランドを破棄するため、通常は、最初のオペランドに望ましい副作用がある場合にのみ有用です。第1オペランドへの副作用が発生しない場合、コンパイラーは、効果なしで式に関する警告を生成する場合があります。
したがって、上記の式では、左端のi
が評価され、その値は破棄されます。次に、++i
が評価され、i
が1増加し、再び式++i
の値が破棄されますが、i
への副作用は永続的です。その後、1
が評価され、式の値は1
になります。
それは同等です
i; // Evaluate i and discard its value. This has no effect.
++i; // Evaluate i and increment it by 1 and discard the value of expression ++i
i = 1 + 1;
上記の式は完全に有効であり、未定義の動作を呼び出しません。これは、左と右の評価の間に シーケンスポイント があるためです。コンマ演算子のオペランド。
C11
、章6.5.17
、 カンマ演算子 から引用
コンマ演算子の左オペランドは、無効な式として評価されます。評価と右側のオペランドの評価の間にシーケンスポイントがあります。次に、右側のオペランドが評価されます。結果にはそのタイプと値があります。
だから、あなたの場合、
(i, ++i, 1)
として評価される
i
、void式として評価され、値は破棄されます++i
、void式として評価され、値は破棄されます1
、値が返されました。したがって、最終的なステートメントは次のようになります
i = 1 + 1;
i
は2
に到達します。これで両方の質問に答えられると思いますが、
i
が値2を取得する方法注:FWIW、左側のオペランドの評価後にシーケンスポイントが存在するため、(i, ++i, 1)
などの式は呼び出されませんUB、1つが一般的に誤って考える.
i = (i, ++i, 1) + 1;
段階的に分析してみましょう。
(i, // is evaluated but ignored, there are other expressions after comma
++i, // i is updated but the resulting value is ignored too
1) // this value is finally used
+ 1 // 1 is added to the previous value 1
したがって、2を取得します。そして、最終的な割り当ては次のとおりです。
i = 2;
iにあったものはすべて、今では上書きされます。
の結果
(i, ++i, 1)
は
1
For
(i,++i,1)
,
演算子が評価された値を破棄し、1
である右端の値のみを保持するように評価が行われます
そう
i = 1 + 1 = 2
カンマ演算子 のwikiページでいくつかの優れた読書を見つけることができます。
基本的には、
...最初のオペランドを評価して結果を破棄し、2番目のオペランドを評価してこの値(および型)を返します。
この意味は
(i, i++, 1)
次に、i
を評価し、結果を破棄し、i++
を評価し、結果を破棄し、1
を評価して返します。
ここで、コンマ演算子が何をしているのかを知る必要があります。
あなたの表現:
(i, ++i, 1)
最初の式i
が評価され、2番目の式++i
が評価され、3番目の式1
が式全体に対して返されます。
結果はi = 1 + 1
です。
ボーナス質問については、ご覧のとおり、最初の式i
はまったく効果がないため、コンパイラーは文句を言います。
コンマの優先順位は「逆」です。これは、古い本やIBMのCマニュアル(70年代/ 80年代)から得られるものです。したがって、最後の「コマンド」は親式で使用されるものです。
現代のCでは、その使用は奇妙ですが、古いC(ANSI)では非常に興味深いものです。
do {
/* bla bla bla, consider conditional flow with several continue's */
} while ( prepAnything(), doSomethingElse(), logic_operation);
すべての操作(関数)は左から右に呼び出されますが、条件「while」の結果として最後の式のみが使用されます。これにより、条件チェックの前に実行するコマンドの一意のブロックを保持するための「goto」の処理が防止されます。
編集:これは、左オペランドのすべてのロジックを処理し、論理結果を返す可能性がある処理関数の呼び出しも回避します。 Cの過去にはインライン関数がなかったことを思い出してください。したがって、これにより呼び出しのオーバーヘッドを回避できます。