15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

こんな書き方すると後置インクリメントは思っても無い動きをする

Last updated at Posted at 2023-07-01

はじめに

C#でこんな書き方すると後置インクリメントは思っても無い動きをするので何故だろうと思い動作検証する事にしました。

対象コード

後置インクリメント対象の変数を代入先変数に指定するとどうなるか

x = x++;

普通は後置インクリメント対象変数と代入先変数は別物にするのでこういう書き方しないですが、
なんとなくこれでも変数Xはインクリメントされそうな気がしますよね。

TL;DR

以下のコードを実行して実行結果を見てみましょう

int x = 0;
Console.WriteLine($"初期化後のXは{x}です。");
x = x++;
Console.WriteLine($"'X = X++'後のXは{x}です。");

実行結果は以下のようになります。
何と、Xはインクリメントされずに『0』のままという結果になります。
image.png

なぜこうなるのか

マイクロソフトのドキュメントにはこのように書かれています。

後置インクリメント(x++)または後置デクリメントx--演算のランタイム処理は、以下のステップからなる

  • xが変数に分類された場合
    • xが評価されて変数が生成される。
    • xの値が保存される。
    • 保存されたxの値は、選択された演算子のオペランド型に変換され、この値を引数として演算子が呼び出される。
    • 演算子から返された値はXの型に変換され、xを先に評価した場所に保存される。
    • 保存されたxの値が演算の結果となる。

今回の例で重要なのは太字の部分になります。
xがまず保存され、その保存された値が代入先の変数に返される。
よって、0が保存されて、その値は代入先に返されるのでxは0のままと言う事になります。

結論

後置インクリメント対象の変数を代入先変数に指定するな!!
この書き方はダメな書き方なので気を付けてください。

修正案

その1

x++;

まぁ、これで良いですよね。

その2

x += 1;

その3

x = ++x;

前置インクリメントはちゃんと動作します

動作検証

今回どうやって動作検証するかですが、 コンパイル結果のIL(.NETの中間言語:Intermediate Language)を見てみる事にします。

SharpLabというサイトを使えば自分で書いたコードをIL簡単にすることができるので今回はそのサイトを利用します。

検証コード

以下のコードをILにしてみます

using System;
public class C {
    public void M() {
        int x = 0;
        x = x++;
    }
}

生成されたILの説明

.method public hidebysig 
    instance void M () cil managed 
{
    .maxstack 3
    .locals init (
        [0] int32 x
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    IL_0003: ldloc.0
    IL_0004: dup
    IL_0005: ldc.i4.1
    IL_0006: add
    IL_0007: stloc.0
    IL_0008: stloc.0
    IL_0009: ret
}

IL_####:は説明では不要なので無視します。
右側がIL命令になるのでそちらを見ていきます。

IL命令の説明
IL命令 説明
ldc.i4.0 整数値0をint32型としてスタックに入れる
ldc.i4.1 整数値1をint32型としてスタックに入れる
stloc.0 スタックから値を取り出し、インデックス0の変数に格納する
ldloc.0 インデックス0の変数をスタックに読み込みする
dup スタックの一番上の値をコピーし、そのコピーをスタックに入れる
add スタックから2つ値を取り出して加算し、結果をスタックに入れる
ret 現在のメソッドから戻り、呼び出し先のスタックから呼び出し元のスタックに戻り値 (存在する場合) を入れる
nop スタックの遷移動作は定義されていないので、今回は無視して大丈夫

IL命令の実行イメージ
image.png

MSのドキュメントの説明通りに保存した値で最後上書きしてしまうため、最終的にx(インデックス[0])は0という結果になります。

参考サイト

15
11
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?