254

次のコードを使用します (コンソール アプリケーションとして使用できます)。

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

の結果iは 0 です。私は 2 を期待していました (同僚の何人かがそうしたように)。iおそらくコンパイラは、結果がゼロになるある種の構造を作成します。

私が 2 を予想した理由は、私の考えでは、右側のステートメントが最初に評価され、i が 1 ずつインクリメントされるためです。i はすでに 1 であるため、1 に 1 を足しています。つまり、1 + 1 = 2 です。明らかに、これは起こっていることではありません。

コンパイラが何をするか、または実行時に何が起こるか説明できますか? なぜ結果はゼロなのですか?

ある種の免責事項: このコードを使用しない (そしておそらく使用すべきではない) ことは完全に承知しています。私は決してそうしないことを知っています。それでも、なぜそのように振る舞うか、正確に何が起こっているのかを知ることは興味深いと思います。

4

24 に答える 24

195

実行中のコードの分解:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

同等のコード

次のコードと同じコードにコンパイルされます。

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

2 番目のコードの逆アセンブル (同じであることを証明するため)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

分解ウィンドウを開く

ほとんどの人は、Visual Studio の [逆アセンブリ]ウィンドウを使用して最終的なインメモリ アセンブリ コードを表示できることを知らないか、覚えていません。実行されているマシンコードを示していますが、CIL ではありません。

デバッグ中にこれを使用します。

Debug (menu) -> Windows (submenu) -> Disassembly

では、postfix++ で何が起こっているのでしょうか?

postfix++ は、評価後にオペランドの値をインクリメントしたいことを示しています...誰もが知っている...少し混乱しているのは、「評価後」の意味です。

では、「評価後」とは次のことを意味します。

  • コードの同じ行でのオペランドの他の使用法は、影響を受ける必要があります。
    • a = i++ + i2 番目の i はインクリメントの影響を受けます
    • Func(i++, i)2番目の私は影響を受けます
  • ||同じ行の他の使用法は、 andのような短絡演算子を尊重し&&ます:
    • (false && i++ != i) || i == 03 番目の i は評価されないため、i++ の影響を受けません。

では、次の意味は何ですか: i += i++;?

と同じですi = i + i++;

評価の順序は次のとおりです。

  1. i + i (つまり 0 + 0) を格納します。
  2. i をインクリメントする (i が 1 になる)
  3. ステップ 1 の値を i に割り当てます (i は 0 になります)

増分が破棄されているわけではありません。

の意味は何ですか: i = i++ + i;?

これは前の例と同じではありません。3番目iはインクリメントの影響を受けます。

評価の順序は次のとおりです。

  1. i を格納 (つまり 0)
  2. i をインクリメントする (i が 1 になる)
  3. ステップ 1 + i (つまり 0 + 1) の値を格納します。
  4. ステップ 3 の値を i に代入します (i は 1 になります)
于 2012-11-22T17:33:17.760 に答える
61
int i = 0;
i += i++;

は次のように評価されます。

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

つまりi、2 回変更されます。1 回はi++式によって、もう 1 回は+=ステートメントによって変更されます。

しかし、+=ステートメントのオペランドは

  • ( の左辺)iの評価前の値とi+++=
  • (の右辺)iの評価前の値。i+++=
于 2012-11-22T16:31:29.583 に答える
32

これは単純に、抽象構文ツリーの左から右へのボトムアップ評価です。概念的には、式のツリーは上から下にたどられますが、再帰が下からツリーに戻ってくると、評価が展開されます。

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

評価は、ルート ノードを考慮することから始まります+=。それが表現の主要な構成要素です。の左オペランドを+=評価して、変数を格納する場所を決定し、以前の値であるゼロを取得する必要があります。次に、右側を評価する必要があります。

右側はポストインクリメント++演算子です。iこれには、値のソースとして、および値が格納される場所として評価される1 つのオペランドがあります。演算子は を評価iして を見つけ0、結果として1その場所に a を格納します。以前の値を返すという0セマンティクスに従って、以前の値 を返します。

これで、制御はオペレーターに戻り+=ます。これで、操作を完了するためのすべての情報が得られました。結果を格納する場所 ( の格納場所i) と以前の値を認識しており、以前の値に追加する値、つまり を持っています0。だから、iゼロで終わります。

Java と同様に、C# は評価の順序を固定することで、C 言語の非常に愚かな側面を無害化しました。左から右、ボトムアップ: コーダーが期待する可能性が最も高い順序です。

于 2012-11-22T18:19:55.200 に答える
12

簡単な答え

int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0;
于 2012-11-23T05:34:50.820 に答える
8

C#が行っていること、および混乱の「理由」

また、値が1になることを期待していました...しかし、その問題に関するいくつかの調査により、いくつかのポイントが明らかになりました。

次の方法を検討してください。

    static int SetSum(ref int a, int b) { return a += b; }

    static int Inc(ref int a) { return a++; }

i += i++と同じだと思っていましSetSum(ref i, Inc(ref i))た。このステートメントの後のiの値は1です:

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

しかし、それから私は別の結論に達しました...i += i++実際には...と同じi = i + i++なので、これらの関数を使用して、別の同様の例を作成しました。

    static int Sum(int a, int b) { return a + b; }

    static int Set(ref int a, int b) { return a = b; }

これを呼び出した後Set(ref i, Sum(i, Inc(ref i)))、iの値は0です:

int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0

これは、C#が何をしているのかを説明するだけでなく、私を含めて多くの人がC#と混同した理由も説明しています。

于 2012-11-23T19:52:20.253 に答える
7

これについて私がいつも覚えている良いニーモニックは次のとおりです。

式の後++に立つ場合は、の値を返します。したがって、次のコード

int a = 1;
int b = a++;

は 1 です。aは 1だったので、は 1 に増えました。人々はこれをポストフィックス表記と呼んでいます。また、置記法もあり、物事は正反対です: ifがの前にある場合、式は操作の値を返します:++ a++

int a = 1;
int b = ++a;

bここに 2 つあります。

したがって、コードの場合、これは

int i = 0;
i += (i++);

i++(上記のように) 0 を返すので、0 + 0 = 0.

i += (++i); // Here 'i' would become two

Scott Meyersは、「効果的な C++ プログラミング」で、これら 2 つの表記法の違いについて説明しています。内部的に、i++(postfix) は以前の値iを記憶し、接頭表記 ( ++i) を呼び出して古い値 を返しますi++iこれが、常にinループを使用する必要がある理由です(最新のコンパイラはすべてinループに変換されforていると思いますが)。i++++ifor

于 2012-11-23T10:52:10.637 に答える
4

変数の後の ++ 演算子は、後置インクリメントにします。インクリメントは、ステートメント内の他のすべて、追加および割り当ての後に発生します。代わりに、変数の前に ++ を置くと、i の値が評価される前に発生し、期待される答えが得られます。

于 2012-11-22T16:27:16.890 に答える
4

計算の手順は次のとおりです。

  1. int i=0//0に初期化
  2. i+=i++ //方程式
  3. i=i+i++ //コンパイラで式を簡略化した後
  4. i=0+i++ //i値置換
  5. i=0+0 //i++ は以下で説明するように 0 です
  6. i=0 //最終結果 i=0

ここで、最初の の値iは 0 です。WKT は、最初に値をi++使用しi、次に値を 1 ずつ増やします。したがって、計算中に値 0 をi使用し、次に 1 ずつ増やします。したがって、値が得られます。 0の。ii++

于 2012-11-23T13:25:53.017 に答える
3

非常に注意してください: C FAQを読んでください: あなたがやろうとしていること (割り当てと++同じ変数の混合) は未指定であるだけでなく、未定義でもあります (つまり、コンパイラは評価時に何でもする可能性があります! 「合理的な」結果)。

セクション 3をお読みください。このセクション全体を読む価値があります。特に 3.9 では、未指定の意味が説明されています。セクション 3.3 では、"i++" などでできることとできないことを簡単にまとめています。

コンパイラの内部構造に応じて、0、2、1、またはその他の値が返される場合があります。そして、それは未定義なので、そうしても問題ありません。

于 2012-11-23T12:39:08.080 に答える
3

上記の回答には多くの優れた推論があります。私は小さなテストを行ったので、あなたと共有したいと思います

int i = 0;
i+ = i++;

ここで結果 i は 0 の結果を示しています。以下のケースを検討してください:

ケース 1:

i = i++ + i; //Answer 1

以前、上記のコードはこれに似ていると思っていたので、一見すると答えは 1 で、実際にはこれに対する i の答えは 1 です。

ケース 2:

i = i + i++; //Answer 0 this resembles the question code.

ここでは、加算前に i++ が実行される可能性がある以前のケースとは異なり、インクリメント演算子は実行パスに含まれていません。

これが少し役立つことを願っています。ありがとう

于 2014-09-09T13:25:21.733 に答える
3

次の 2 つのオプションがあります。

最初のオプション: コンパイラが次のようにステートメントを読み取る場合、

i++;
i+=i;

結果は 2 です。

為に

else if
i+=0;
i++;

結果は 1 です。

于 2012-11-22T16:29:07.797 に答える
2

簡単に言えば、

i++ は、「+=」演算子が完了した後、「i」に 1 を追加します。

必要なのは ++i で、「+=」演算子が実行される前に「i」に 1 が追加されます。

于 2012-11-28T17:06:04.120 に答える
2

Cプログラミングの101タイプの観点からこれに答えたいと思っています。

私には、次の順序で起こっているように見えます。

  1. iは 0 として評価されi = 0 + 0、インクリメント操作はi++「キューに入れられた」ことになりますが、0 の割り当てもiまだ行われていません。
  2. インクリメントi++が発生する
  3. 上記の割り当てi = 0が行われ、#2 (ポストインクリメント) が行ったであろうことはすべて効果的に上書きされます。

現在、#2 は実際には発生しない可能性があります (おそらく発生しませんか?)。これは、コンパイラが目的を果たさないことを認識している可能性が高いためですが、これはコンパイラに依存する可能性があります。いずれにせよ、他のより知識のある回答は、結果が正しく、C# 標準に準拠していることを示していますが、ここで C/C++ で何が起こるかは定義されていません。

その方法と理由は私の専門知識を超えていますが、以前に評価された右側の割り当てがポストインクリメントの後に発生するという事実は、おそらくここで混乱を招くものです。

さらに、私が信じる++iのではなく、そうしない限り、結果が 2 になるとは思わないでしょう。i++

于 2012-11-27T19:13:58.887 に答える
0
i=0

i+=i

i=i+1

i=0;

次に、 に 1 が追加されiます。

i+=i++

したがってi、に 1 を追加する前に、値iは 0 でした。前に 1 を追加した場合にのみi、値 0 を取得します。

i+=++i

i=2
于 2012-11-23T16:04:14.567 に答える
-4

答えはiになります1

方法を見てみましょう:

最初はi=0;

i +=i++;次に、 の値に従って計算すると0 +=0++;、 のようなものになるため、演算子の優先順位に従って0+=0が最初に実行され、結果は になります0

次に、インクリメント演算子が as 0++、 as として適用され、0+1の値はiになります1

于 2012-11-23T07:33:20.633 に答える