私はCプログラミング言語の初心者ですが、最近、論理積について読みました&&
演算子。
また、Cプログラミング言語では、ゼロ以外の値はすべて[〜#〜] true [〜#〜]として扱われることも知っています。
NON-ZERO && NON-ZERO = 1
NON-ZERO && ZERO = 0
ZERO && NON-ZERO = 0
ZERO && ZERO = 0
しかし、私が次のプログラムを扱っているとき、私は期待された答えを得ていません。
int main(){
int x, y, z;
x = y = z = -1;
y = ++x && ++y && ++z;
printf("x = %d, y = %d, z = %d, x, y, z);
return 0;
}
私は期待している
x = 0, y = 0, z = 0
しかし答えは
x = 0, y = 0, z = -1
誰かが説明できますか、なぜ私はこの答えを得ているのですか?
編集:この質問では、演算子の優先順位については尋ねていません。
短絡評価 のため、x
が0
の場合、y
とz
は、実際には評価する必要がありません。 0 && ANYTHING
は0
です。
x
が0
にインクリメントされると、結果は0
になり、それがy
になります。
z
は変更されません(-1
)。
x | y | z
----+----+-----
-1 | -1 | -1 //x = y = z = -1;
0 | -1 | -1 //++x && ... Now the whole expression is evaluated to 0
0 | 0 | -1 //y = ++x && ++y && ++z;
&&
が短絡で評価されることだけを考えることができます:A && B
が与えられた場合、A
がfalse
を評価する場合はB
は評価されません。
そう:
X
は0
になります。 && ++y && ++z
は、X/0/false && ...
以降、評価されません。
y=0
から割り当てられたy = x/0/false
++z
は実行されないため、z
は変更されません。
完全性のために(ブレインダンプ):
この魔術の背後にある用語は 短絡 と呼ばれます。コードを調べてから、これが発生する理由について簡単に説明しましょう。見つめている:
int main( void ) {
int x, y, z;
x = y = z = -1;
y = ++x && ++y && ++z;
printf( "x = %d, y = %d, z = %d, x, y, z );
return 0;
}
...私たちはそれを1行ずつ分解し始めます。最初の行:
int x, y, z;
... 3つの整数、x
、y
、およびz
を宣言します。初期化(代入演算子)がないため、スタックフレーム上でガベージ値に初期化されます。この行は実際には重要ではありません。次の行を見てみましょう。
x = y = z = -1;
...同じ行で複数の割り当てを行っていることがわかります。代入演算子は(代入演算子の右側の値を使用して)代入演算子の左側の識別子を変更し、x
の値を返すことを思い出してください。これは、割り当てのオーバーロードとして知られています。しかし、繰り返しになりますが、これは実際には重要ではありません。認識すべき唯一の重要なことは、x
、y
、およびz
がすべて-1になったことです。次の行を見てみましょう:
y = ++x && ++y && ++z;
...ソーサリーヨーダは言います。どのステップが最初に評価されているかをより明確にするために、括弧を追加しましょう。
y = ( ( ++x ) && ++y && ++z );
...ここで最も内側の括弧を見ると、それがx
のプレフィックス増分であることがわかります。つまり、x
の値をインクリメントしてから返します。 x
は元々-1でしたが、インクリメントされた後は0になっていることに注意してください。これは次のように解決されます。
y = ( 0 && ++y && ++z );
...ここで、真理値表を見ると次のことに注意することが重要です。
A | B | A && B
--------------
T | T | T
T | F | F
F | T | F
F | F | F
... AND
論理演算子の場合、F(AND)T、T(AND)Fの両方がFであることがわかります。コンパイラは、接続詞(AND)を評価するときに、これと短絡を認識します。値はfalse
-賢い最適化手法です。次に、y
を0に割り当てることに解決されます(これはfalseです)。 Cでは、ゼロ以外の値はtrue
であり、0のみがfalse
であることを思い出してください。行は次のようになります。
y = 0;
...次の行を見てください:
printf( "x = %d, y = %d, z = %d, x, y, z );
... x = 0, y = 0, z = -1
が出力されることは明らかです。
&&
演算子はペアごとに評価されるため、Cが評価していると思います
__コード__
現在、((++x && ++y) && ++z)
はゼロを返すため、最初の++x
は失敗し、2番目の&&
は失敗し、++y
または++z
を評価する必要はありません。
y = 0
それは式の結果だからです。
z
は触れられていません
何が起こるかというと、最初の部分はy
の新しい値がどうなるかをすでに保証しているため、++y
と++z
は評価されません。
ステートメントの最初の部分は++x && ...
です。これは0 && ...
と同等です。その後、y
が最後に0になることがわかっているため、ステートメントの残りの部分は実行されません。
これを行った場合:
int main(){
int x,y,z,tmp;
x = y = z = -1;
tmp = ++x && ++y && ++z;
printf("x = %d, y = %d, z = %d, tmp = %d", x,y,z, tmp);
return 0;
}
x = 0, y = -1, z = -1, tmp = 0
を取得します
左側の評価はC99標準で保証されています。セクション6.5.13 Logical AND operator
で見つけることができます
ビット単位の二項&演算子とは異なり、&&演算子は左から右への評価を保証します。最初のオペランドの評価の後にシーケンスポイントがあります。最初のオペランドが0と等しい場合、2番目のオペランドは評価されません。
Wikipedia またはC99標準のAnnex Cで、シーケンスポイントとは何かに関する詳細情報を見つけることができます。