web-dev-qa-db-ja.com

なぜ「if(i ++ &&(i == 1))」が偽で、iが値1を保持するintなのですか?

{
    int i = 1;
    if (i++ && (i == 1))
        printf("Yes\n");
    else
        printf("No\n");
}

私の理解では、if条件では、最初に式(i==1)が評価され、1、そして論理的に1これはiの値であるため、式は1 && 1 == 1が、else部分が実行されます。

誰かがelse部分が実行される理由を説明できますか?

44
Tahir Sanglikar

いいえ。Cでは、&&演算子のLHSの評価とRHSの評価の間に シーケンスポイント があり、RHSを評価する前に増分を実行して完了する必要があります。したがって、i++i++ != 0と同等)が実行され、インクリメントが完了し(そして式がtrueに評価されます)、RHSが評価されるまでにi == 2なので、式全体がfalseになり、「いいえ」が出力されます。 &&演算子のLHSがfalse(0)に評価された場合、&&演算子の 'short-circuit'プロパティのため、RHSは評価されません。

LHSとRHSの評価の間にシーケンスポイントを持つプロパティを持つ少数の演算子:&&||、および,(引数リストの区切りとしてではなく、演算子として)—そして? :もあります。 t二項演算子ですが、条件が評価された後、?の後の式または:の後の式のいずれかの前にシーケンスポイントがあります(両方ではなく、どちらか一方が常に評価されます)。

&&および||演算子は、 'short-circuit'プロパティを持つ唯一の演算子です。 &&のRHSは、LHSがtrueと評価された場合にのみ評価されます。 ||のRHSは、LHSがfalseと評価された場合にのみ評価されます。


シーケンスポイントの明確化

Iwillnotexist Idonotexist 正しく アサート済み

C11標準ではシーケンスポイントが廃止されておらず、C++ 11標準のみが廃止されています。

C++ 11(ISO/IEC 14882:2011)によると:

1.9プログラムの実行

¶13Sequenced beforeは、単一のスレッド(1.10)によって実行される評価間の非対称で推移的なペアワイズ関係であり、これらの評価の間に半順序を誘導します。任意の2つの評価[〜#〜] a [〜#〜]および[〜#〜] b [〜#〜]の場合、[〜# 〜] a [〜#〜][〜#〜] b [〜#〜]の前にシーケンスされ、次に[〜#〜] a [〜#の実行〜]は、[〜#〜] b [〜#〜]の実行に先行します。 [〜#〜] a [〜#〜]の前に[〜#〜] b [〜#〜]および[〜#〜 ] b [〜#〜]は、[〜#〜] a [〜#〜]の前にシーケンスされません。その後、[〜#〜] a [〜#〜]および[〜#〜] b [〜#〜]nsequencedです。 [注:シーケンスなしの評価の実行は重複する場合があります。 —end note]評価[〜#〜] a [〜#〜]および[〜#〜] b [〜#〜]不定に順序付けいずれかの場合[〜#〜] a [〜#〜]の前に順序付け[〜#〜] b [〜#〜]または[〜#〜] b [〜#〜]は、[〜#〜] a [〜#〜]の前にシーケンスされますが、どちらが指定されているかは指定されていません。 [注:不定にシーケンスされた評価は重複できませんが、どちらかを最初に実行できます。 —メモの終了]

「シーケンスポイント」という用語は、C++ 11にはまったく表示されません(近い一致は「シーケンスポインタ」のみです)。

C11(ISO/IEC 9899:2011)によると:

5.1.2.3プログラムの実行

¶3Sequenced beforeは、単一のスレッドによって実行される評価間の非対称で推移的なペアワイズ関係であり、これらの評価の間に半順序を誘導します。任意の2つの評価[〜#〜] a [〜#〜]および[〜#〜] b [〜#〜]の場合、[〜# 〜] a [〜#〜][〜#〜] b [〜#〜]の前にシーケンスされ、次に[〜#〜] a [〜#の実行〜]は、[〜#〜] b [〜#〜]の実行に先行します。 (逆に、[〜#〜] a [〜#〜][〜#〜] b [〜#〜]の前にシーケンスされている場合、[ 〜#〜] b [〜#〜]後のシーケンス[〜#〜] a [〜#〜]です。)[〜 #〜] a [〜#〜]は、[〜#〜] b [〜#〜]の前後にシーケンスされません。その後、[〜#〜] a [〜 #〜]および[〜#〜] b [〜#〜]nsequencedです。評価[〜#〜] a [〜#〜]および[〜#〜] b [〜#〜]は、不定シーケンス when- [〜#〜] a [〜#〜]は、[〜#〜] b [〜#〜]の前後にシーケンスされますが、どちらが指定されているかは指定されていません。13) 式の評価の間にシーケンスポイントが存在する[〜#〜] a [〜#〜][〜#〜] b [〜# 〜]は、[〜#〜] a [〜#〜]に関連するすべての値の計算と副作用が、[〜 #〜] b [〜#〜]。 (シーケンスポイントの概要は、付録Cに記載されています。)

13) シーケンスなしの評価の実行はインターリーブできます。不定にシーケンスされた評価はインターリーブできませんが、任意の順序で実行できます。

そのため、C11はシーケンスポイントを保持しますが、C++ 11と本質的に同じ用語を使用して、「前にシーケンスされた」および関連する用語を追加します。

92

ここでは、簡単な説明

enter image description here

それがこの条件が「偽」になる理由です

40
Sruit A.Suk

いつ &&は式で使用され、その引数は左から右に評価であることが保証されています。したがって、iの値は2 いつ (i==1)が評価されます。したがって、式は偽であり、else部分が実行されます。

ただし、完全性のために、左の引数がfalseまたは0と評価される場合、右の引数はまったく評価されないことに注意してください。

28
user3821934

1 && 1 = 1および1 && 0 = 0は明らかだと思います。マイケルLの答えは私にはいいようですが、それでも少し詳しく説明してみます。演算子の優先順位リストを提供するリンクは次のとおりです。

http://www.difranco.net/compsci/C_Operator_Precedence_Table.htm

このリンクにアクセスしてその表を参照すると、&&が左から右への結合性を持っていることがわかります。そのため、最初に左部分の後で2になります(Sruitは図を使用してこれを表示しようとしました)。 1つのチェックが完了しました。次のようなコードを記述することでこれをチェックできます。

このコードは、実行フローがi == 1の部分に達するとi = 2になることを説明しています。

#include <stdio.h>
int main() {

    int i = 1;
    if (i++ && (printf("%d\n",i)))
        printf("Yes\n");
    else
        printf("No\n");
    return 0;
}

したがって、出力は次のとおりです。

2

はい

したがって、2 == 1が偽であることが判明し、最終的に答えは驚くべき感触を与えます!!!右側のブラケットは0、左側は1なので、1 && 0 = 0になります。これは理解するには十分だと思います。

1
codecracker

ループ文ではi ++がポストインクリメントに使用されているため、混乱しています。例えば.

for (i=0;i<1;i++)

上記のループプログラムでは、iは最初に初期値を取得し、条件をチェックします。 if条件はtrueであるため、iをインクリメントし、iをループ本体の古い値に評価しますが、ループ本体の外側には新しい値があります- 1

あなたの質問ではifを使用しており、iの古い値のスコープは論理的に演算子に遭遇すると終了し、自動的に新しい値を取得し、2に増分しますfalseを返します。

0
Muhammad Nasir