web-dev-qa-db-ja.com

Prefix(++ x)およびPostfix(x ++)操作はどのように機能しますか?

誰かがプレフィックス/ポストフィックス演算子が実際にどのように機能するか教えてもらえますか?私はオンラインでよく探していますが、何も見つかりませんでした。

私が言うことができることから、prefexは最初に増分し、次に操作を実行し、次に割り当てます。
Postfixは最初に操作を実行し、次に割り当ててからインクリメントします。

しかし、コードに少し問題があります。

int x, y;
x = 1;
y = x + x++; // (After operation y = 2)(x=2)

しかし、私がそうするとき:

y = x++ + x; // (After operation y = 3)(x=2)

これらの操作がなぜ異なるのかわかりません。 2つの質問があります:

  • 違いを説明していただけますか?

  • これは他のオペレータープレフィックスにどのように適用されますか?

26
GodsCrimeScene
  • C#では、+のオペランドが左から右の順序で評価されます。
  • CおよびC++では、+のオペランドの評価順序は指定されていません。

C#の場合、例は次のように機能します。

 y = x + x++;
     ^ x is 1
         ^ x is increased to 2, but the postfix increment returns the old value (1)
 y = 2

 y = x++ + x;
     ^ x becomes 2, but postfix increment returns the old value (1)
           ^ x is now 2 here
 y = 3
28
Mark Byers

この質問はかなりの量で尋ねられます。誰かがこの質問をするたびに、非常に多くの人が間違った答えを投稿することに注意してください。プログラミングの本を書いて他の人に虚偽を教える人を含め、多くの人がこれらの演算子がどのように機能するかについて間違った考えを持っています。ここで他の答えを注意深く読んでください。

C#の動作の正しい分析については、以下を参照してください。

i ++と++ iの違いは何ですか?

C++の場合、副作用が観察されている場合は、どのような動作でも正しい動作です。 C++は、インクリメントの副作用がいつ表示されるかを定義していません。2つのコンパイラーは異なる方法でそれを行うことができます。

従うべき良いルールは、副作用が発生する順序に依存しないことですany言語ですが、確かに C++では信頼できないため、それに依存しないでください。

あなたの特定のケースを見るには:

int x, y;     
x = 1;     
y = x + x++; 

Xとyが両方とも2であると報告しました。これはC#では正しいです。 C#での正しい動作は次のとおりです。

  • yを変数として評価する
  • xを値として評価します-それは1です
  • x ++を値として評価します。これは、xを変数として評価し、元の値である1を取り、その値である2をインクリメントし、次に2をxに割り当てて、元の値である1になります。
  • 1 + 1、つまり2を評価します
  • yに2を割り当てます。

したがって、C#ではxとyはどちらも2です。

C++でも同じことができますが、加算を右から左の順序で評価することができます。つまり、次のことを行うことが許可されています。

  • x ++を値として評価します。これは、xを変数として評価し、元の値である1を取り、その値である2をインクリメントし、次に2をxに割り当てて、元の値である1になります。
  • xを値として評価します-それは2です
  • 1 + 2、つまり3を評価します
  • yを変数として評価する
  • yに3を割り当てます。

C++でもこれを行うことができます。

  • x ++を値として評価します。これはxを変数として評価し、元の値である1を取り、その値をインクリメントします。これは2です...ここではステップが欠落しています...そして元の値である1になります。
  • xを値として評価します-それは1です
  • 1 + 1、つまり2を評価します
  • xに2を割り当てます-以前に欠落していたステップ。
  • yを変数として評価する
  • yに2を割り当てます。

したがって、C++では、コンパイラの作成者の気まぐれに応じて、yを3または2として取得できます。 C#では、常にyが2であることがわかります。C++では、インクリメントの割り当ては、発生する限り、いつでも発生する可能性があります。 C#では、インクリメントの割り当てが発生する必要がありますafterインクリメントされた値が計算され、before元の値が使用されます。 (実行中のスレッドから観察した場合。別のスレッドからこのようなものを観察しようとすると、すべての賭けが無効になります。)

2番目の例では:

y = x++ + x; 

C#で必要な動作は次のとおりです。

  • yを変数として評価する
  • x ++を値として評価します。これは、xを変数として評価し、元の値である1を取り、その値である2をインクリメントし、次に2をxに割り当てて、元の値である1になります。
  • xを値として評価します-それは2です
  • 1 + 2、つまり3を評価します
  • yに3を割り当てます。

したがって、C#での正解は、yが3、xが2であるということです。

この場合も、C++はこれらの手順を任意の順序で実行できます。 C++では次のことが許可されています。

  • xを値として評価します-それは1です
  • x ++を値として評価します。これは、xを変数として評価し、元の値である1を取り、その値である2をインクリメントし、次に2をxに割り当てて、元の値である1になります。
  • 1 + 1、つまり2を評価します
  • yを変数として評価する
  • yに2を割り当てます。

繰り返しますが、C++では、コンパイラの作成者の気まぐれに応じて、yは2または3です。 C#では、正解はyが3であるということです。

60
Eric Lippert

どちらの場合も、xが使用された後に増分が適用されました。最初に、次のように評価されました:y = 1 + 1(2にインクリメント)

2番目に

y = 1(2にインクリメント)+2。

それがあなたが異なる答えを得た理由です。

6
Lou

CおよびC++の場合:
出力は詳細不明です。

参照-C++ 03標準:

セクション5:式、パラ4:

特に記載のない限り[例: &&と||]の特別な規則、個々の演算子のオペランドと個々の式の部分式の評価の順序、および副作用が発生する順序は指定されていません。

C99セクション6.5

「演算子とオペランドのグループ化は構文で示されます。72)後で指定される場合を除き(関数呼び出し()、&&、||、?:、およびコンマ演算子の場合)、部分式の評価の順序と順序副作用が発生する場所はどちらも特定されていません。」

6
Alok Save

x++++xには、結果(または値)と副作用の両方があります。

議論を整数型のオペランドに限定すると、x++の-​​resultは、xの現在の値が何であれです。 副作用xを1インクリメントすることです。したがって、コードが与えられます。

x = 0;
y = x++;

結果はx == 1およびy == 0になります(xおよびyが整数型であると想定)。

++xの場合、resultは1に現在の値xを加えたものです。 副作用xを1インクリメントすることです。したがって、コードが与えられます。

x = 0;
y = ++x;

結果はx == y == 1になります。

CおよびC++とC#の違いは、オペランドが評価されるときと、副作用が適用されるときです。 C#は、式のオペランドが常に左から右に評価されることを保証します。 CおよびC++は、&&||?:、コンマ、および関数呼び出し()演算子の左から右への評価のみを保証します-他のすべての演算子については、オペランドが評価される順序は未指定です。

同様に、C#では、x++++xの副作用は、式が評価された直後に適用されますが、CとC++では、次のシーケンスポイント

評価に関するC#のルールは、x = x++a = b++ * b++a[i] = i++などの式が明確に定義されていることを保証しますが、CおよびC++言語の定義では、そのような式はndefined動作(任意の結果が可能です)。

4
John Bode

x + x ++およびx ++ + xは、依存したくない病的な副作用の例です。 x ++と++ xはどちらもxをインクリメントしますが、xを追加する際の評価の順序は定義されていません。コンパイラは、最初に評価する「側」を選択できます。

3
n8wrl

考えてみましょう:

y = x + x++;

その動作が定義されているかどうかに関係なく(CおよびC++では定義されていません。明らかにC#で十分に定義されています)、何をしようとしても、それを表現するためのより良い方法があるはずです。

厳密な左から右への評価を想定している場合、上記は次のように書くことができます。

y = x * 2;
x ++;

=*、および++の意味を知っている読者にとっては、その意味は明確で明確であり、コードの将来の保守担当者はあなたを追い詰めようとはしません。

または、コンパイラが効率的なコードを生成することを信頼していない場合は、x + xまたはx << 1と書くことができますが、そのような不信は通常見当違いです。

あなたが主張するなら、あなたは書くことさえできます:

y = x++ * 2;

それは私の個人的な好みには少し簡潔ですが、それでも明確です。

他の誰かのコード(確かにプログラマーが多くの時間を費やしていることです)を理解したい場合は、複雑な式を理解することが重要になる可能性があります。ただし、独自のコードを作成する場合は、キーストロークを保存する(または演算子の優先順位チャートをどれだけよく知っているかを示す)よりも明確にすることが重要です。

0
Keith Thompson