3

与えられた...

int a = 1, b = 4;

それで...

a += b += a += b;  

C++で評価されます...

(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  

...そしてC#で...

(a += (b += (a += b))); // a = 10[1 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
                        //        ^ re-use of original value of a (1)  
                        //          instead of expected intermediate right-to-left  
                        //          evaluation result (5)  

上記はVisualStudio2008と2012の両方でテストされているため、最近導入された言語のバグの疑いはありません。

ただし、C#の動作がC ++の動作を模倣することを期待しており、教育が必要であると想定していました。式評価ロジックの方法を理解しているので、MSILを説明する必要はありません。かなり広範囲に検索し、言語仕様で関連する細かい印刷物を見つけることができなかったので、言語の専門家がなぜそうなのかを説明してくれることを望んでいました。

そして、なぜ地球上でこれをやりたいのか知りたい好奇心旺盛な人のために...このような2つの整数型の非常に効率的で整頓されたインラインスワップのための便利な小さなC++トリックがあります...

a ^= b ^= a ^= b;  

私はそれがC#で機能しないことに失望し、その理由について興味があります。これは、C#の低レベルのメカニズムとその背後にある理論的根拠を理解することについてです。これ以上、それ以下、特に読みやすさの宗教はご遠慮ください。

NB 皆さん、どうぞ、これはすべてを1行にまとめるという深刻な努力ではありません!!!
C(およびそれ以降のC ++)の場合、演算子の優先順位と結合性は常に正確に定義されています。代入演算子の標準的な右から左への結合性により、C / C ++バージョンの動作が100%明確で予測可能になります。それはいくつかのコンパイラとプラットフォームで私のために働きました、そして私はこのように振る舞わなかったコンパイラが故障していると思います。
コードの行が難読化されていることを認めます。これは好奇心であり、ジュニアC /C++プログラマーに提供する可能性のある「頭の体操」として使用されます。明らかにC#は異なります-そして明らかに意図的にそうです。
私は、C#の祖先言語の1つからの動作の相違につながった設計上の考慮事項の説明を求めています。知識に基づいた推測でも歓迎されます。

AlexanderStepaniuk に感謝します。
オペランドの左から右への評価から始めます

a        = 1 + theRest1     // (1)  
theRest1 = b = 4 + theRest2 // (2)  
theRest2 = a = 1 + 4        // (3)  
(3) into (2): theRest1 = b = 4 + 5  
(2) into (1): a = 1 + 9  

「なぜ」の説明はまだありがたいです。
しかし、C#仕様は、上記が正しい評価であることを明確にしています。
そして、以下は、C ++のトリックに2つの変数を使用して(私が思うに)得ることができる限り近いです...

b ^= a ^= b;
a ^ = b;

私はそれが好きではありません-記録のために-それは私の直感を壊すので;-)

4

3 に答える 3

3

C /C++でも動作することが保証されているわけではありません。1つのステートメントで複数回割り当てられた変数の値は未定義です。

それが機能するかどうかは、実装にかかっています。まったく機能しないコンパイラやアーキテクチャを見つけることもできます。

C#はそれについて厳格になっているだけで、それは悪いことではありません。

とにかく、xorトリックは現代建築では無意味です。基礎となる実装は、一時変数を使用するよりも効率が悪くなります。最新のCPUには2つ以上のレジスタがあります。

于 2012-11-24T10:53:15.327 に答える
3

それもcに戻ります。

K&Rには次の例があります。

a[i++] = i;

これは未定義の動作であり、1行で変数を変更および使用します。1つのコンパイラは1つの方法で互いに機能します。標準は、あなたがそれに依存しないように、意図的にそれを未定義のままにします。

K&Rは、個別の行または中間変数を使用することをお勧めします。人間が読みやすいようにするためではなく、コンパイラーが解釈を明確にするためです。

a ^= b ^= a ^= b;

したがって、これは明確になります。

a ^= b; b ^= a; a ^= b; // can still on one line if that is important to you!

未定義の動作については、こちらを参照してください:これらの構成(++を使用)が未定義の動作であるのはなぜですか?

于 2012-11-24T11:06:31.180 に答える
2

あなたは実際に自分でeverithingを説明しました:)

「(1)の元の値の再利用」

なぜそれが起こるのですか?あなたはここで素晴らしい説明を見つけることができます

要するに。
a += anythingと同等a = a + anythingです。
したがって、コンパイラは式の両方のオペランドを評価してからa + anything、結果を1に割り当てる必要がありaます
。最初のオペランドが評価されます(これはa)。評価の結果は12です
。2番目のオペランドが評価されます(これはexpression)。評価の結果は9です。評価の副作用aは5を含むこと
です。3。第1オペランドと第2オペランドの合計が評価​​されます。結果は104です
。結果はに割り当てられaます。現在a、10が含まれています。

お役に立てば幸いです。

UPDATE c#仕様による:式のオペランドが評価される順序は、
から右です。150ページの§14.2を参照してください。 したがって、この動作はC#で指定され、正しいものです。

于 2012-11-24T16:03:41.250 に答える