[解決済み] k += c += k += c;」のインライン演算子について教えてください。
質問
次の操作の結果はどのように説明されますか?
k += c += k += c;
私は以下のコードからの出力結果を理解しようとしていました。
int k = 10;
int c = 30;
k += c += k += c;
//k=80 instead of 110
//c=70
で、現在、私は "k" の結果がなぜ 80 であるのかを理解するのに苦労しています。なぜ k=40 を割り当てると動作しないのでしょうか (実際に Visual Studio はその値が他の場所で使用されていないことを私に教えてくれました)。
なぜ k は 80 であり、110 ではないのですか?
に分割して操作すると。
k+=c;
c+=k;
k+=c;
の場合、結果はk=110となります。
に目を通そうとしたのですが CIL を見ようとしましたが、私は生成されたCILを解釈することにそれほど深くなく、いくつかの詳細を得ることができません。
// [11 13 - 11 24]
IL_0001: ldc.i4.s 10
IL_0003: stloc.0 // k
// [12 13 - 12 24]
IL_0004: ldc.i4.s 30
IL_0006: stloc.1 // c
// [13 13 - 13 30]
IL_0007: ldloc.0 // k expect to be 10
IL_0008: ldloc.1 // c
IL_0009: ldloc.0 // k why do we need the second load?
IL_000a: ldloc.1 // c
IL_000b: add // I expect it to be 40
IL_000c: dup // What for?
IL_000d: stloc.0 // k - expected to be 40
IL_000e: add
IL_000f: dup // I presume the "magic" happens here
IL_0010: stloc.1 // c = 70
IL_0011: add
IL_0012: stloc.0 // k = 80??????
どのように解決するのですか?
以下のような操作で
a op= b;
というのは
a = a op b;
. 代入は文としても式としても使用でき、式としては代入された値が得られます。あなたのステートメント ...
k += c += k += c;
...は、代入演算子が右結合であるため、次のようにも書けます。
k += (c += (k += c));
または(展開)
k = k + (c = c + (k = k + c));
10 → 30 → 10 → 30 // operand evaluation order is from left to right
| | ↓ ↓
| ↓ 40 ← 10 + 30 // operator evaluation
↓ 70 ← 30 + 40
80 ← 10 + 70
ここで、全体の評価の間、関係する変数の古い値が使用されます。これは特に
k
という変数があります (以下の IL のレビューと
リンク
Wai Ha Lee が提供したもの)。したがって、70+40(の新しい値)を得ているわけではありません。
k
)=110ではなく、70+10(旧値の
k
) = 80.
ポイントは、(C#によれば
スペック
)
式中のオペランドは左から右へ評価されます。
(オペランドは変数
c
と
k
のようになる)。これは、この場合、右から左への実行順序を決定する演算子の優先順位や連想性とは無関係です。(Eric Lippertの
答え
へのコメントを参照)。
さて、ILについて見てみましょう。IL はスタックベースの仮想マシンを想定しており、つまりレジスタを使用しません。
IL_0007: ldloc.0 // k (is 10)
IL_0008: ldloc.1 // c (is 30)
IL_0009: ldloc.0 // k (is 10)
IL_000a: ldloc.1 // c (is 30)
スタックは次のようになります (左から右へ。スタックの先頭が右)
10 30 10 30
IL_000b: add // pops the 2 top (right) positions, adds them and pushes the sum back
<ブロッククオート
10 30 40
IL_000c: dup
<ブロッククオート
10 30 40 40
IL_000d: stloc.0 // k <-- 40
<ブロッククオート
10 30 40
IL_000e: add
<ブロッククオート
10 70
IL_000f: dup
<ブロッククオート
10 70 70
IL_0010: stloc.1 // c <-- 70
<ブロッククオート
10 70
IL_0011: add
<ブロッククオート
80
IL_0012: stloc.0 // k <-- 80
なお
IL_000c: dup
,
IL_000d: stloc.0
への最初の代入は
k
への最初の代入は、最適化されるかもしれません。おそらくこれは、IL をマシン コードに変換する際のジッターによって、変数に対して行われるものでしょう。
また、計算で必要とされるすべての値は、代入が行われる前にスタックにプッシュされるか、これらの値から計算されることに注意してください。代入された値 (
stloc
によって) 割り当てられた値は、この評価中に決して再利用されることはありません。
stloc
はスタックの先頭をポップアップします。
次のコンソールテストの出力は、(
Release
モード、最適化オン)
kの評価 (10)
cを評価する (30)
kの評価 (10)
cの評価 (30)
40をkに代入
70はcに割り当て
80はkに割り当て
private static int _k = 10;
public static int k
{
get { Console.WriteLine($"evaluating k ({_k})"); return _k; }
set { Console.WriteLine($"{value} assigned to k"); _k = value; }
}
private static int _c = 30;
public static int c
{
get { Console.WriteLine($"evaluating c ({_c})"); return _c; }
set { Console.WriteLine($"{value} assigned to c"); _c = value; }
}
public static void Test()
{
k += c += k += c;
}
関連
-
[解決済み】「The breakpoint will not currently be hit」を改善するには?このドキュメントにはシンボルが読み込まれていません。" という警告はどうすれば改善されますか?
-
[解決済み】エラー。「戻り値を変更できません」 C#
-
[解決済み] [Entity Framework 4.1でエンティティに関連オブジェクトを追加する際に、エンティティオブジェクトをIEntityChangeTracker.の複数のインスタンスから参照できない。
-
[解決済み】C# ASP.NET使用時に「WebClientのリクエスト中に例外が発生しました。
-
[解決済み] C#の正しいバージョン番号を教えてください。
-
[解決済み] C#がforeachで変数を再利用するのは理由があるのか?
-
[解決済み] URLのPath.Combineは?
-
[解決済み] ファイルが使用中であるかどうかを確認する方法はありますか?
-
[解決済み】C#のyieldキーワードは何に使われるのか?
-
[解決済み] 比較演算子はNULL intに対してどのように作用しますか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み] [Entity Framework 4.1でエンティティに関連オブジェクトを追加する際に、エンティティオブジェクトをIEntityChangeTracker.の複数のインスタンスから参照できない。
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み】「namespace x already contains a definition for x」エラーの修正方法は?VS2010にコンバートした後に発生しました。
-
[解決済み】非静的メソッドはターゲットを必要とする
-
[解決済み】Unity 「関連するスクリプトを読み込むことができません」「Win32Exception: システムは指定されたファイルを見つけることができません"
-
[解決済み] 2つのリストを結合する
-
[解決済み】「namespace」なのに「type」のように使われる。
-
VSでscanfエラーを恒久的に解決するには、ソースファイルを作成し、自動的に#define _CRT_SECURE_NO_WARNINGS 1を追加してください。
-
[解決済み】i++と++iの違いは何ですか?