C# メンバー変数 int number = 1; の内部構造
(インスタンス フィールド/static フィールド/const フィールドの相違点まで)
目次
1. ソースコード上の宣言例
class Sample
{
// インスタンス フィールド
int number = 1;
// static フィールド
static int s_number = 1;
// 定数フィールド
const int C_NUMBER = 1;
}
2. コンパイル時 ― メタデータと IL の違い
種類 | 生成される IL | 初期化コードが入る場所 | メタデータ登録 |
---|---|---|---|
インスタンスnumber | ldarg.0, ldc.i4.1, stfld int32 Sample::number | インスタンス コンストラクタ (.ctor) | Field テーブルに 1 エントリ |
statics_number | ldc.i4.1, stsfld int32 Sample::s_number | 型コンストラクタ(.cctor) | Field テーブルに 1 エントリ |
constC_NUMBER | IL に フィールド記述なし。参照個所はすべて ldc.i4.1 即値 | ― | Constant テーブルにリテラル値だけ登録 |
readonly フィールドはインスタンス/static と同じ IL を出力しますが、値の設定がコンストラクタに限定されるという言語仕様の制約が追加されるだけです。
3. 実行時メモリ配置
3.1 インスタンス フィールド
┌─ヒープ上の Sample オブジェクト────────────┐
│ [8 bytes] オブジェクトヘッダー │
│ [8 bytes] EEType Ptr (64 bit) │
│ │
│ [4 bytes] number = 1 ←★ここ │
│ …(ほかのフィールドが続く)… │
└───────────────────────────────────────┘
- 配置順 はコンパイラがレイアウトを最適化(たとえば 64 bit でのアライメント調整)。
- アクセス命令 は ldfld / stfld。
- オブジェクトが GC に移動されると、フィールドも一緒にスライドします。
3.2 static フィールド
- 型ごとに 1 つだけ確保される領域(“静的ヒープ”)。
- CLR が型を 初回参照する瞬間に .cctor が実行され、値が代入される。
- アクセス命令は ldsfld / stsfld。
- ジェネリック型の場合、型引数ごとに別インスタンスの static が作られる(List<int> と List<string> では別領域)。
3.3 const フィールド
- メモリ領域は存在しません。
- 参照箇所はすべてコンパイル時に即値へ置換されるため、実行時にロードされるものはありません。
4. JIT 後の命令例(x64, Debug ビルドの概念図)
種類 | 代表的な機械語 | コメント |
---|---|---|
インスタンス | mov dword ptr [rcx+offset], 1 | rcx は this ポインタ |
static | mov dword ptr [rip+addr], 1 | 型の静的領域アドレス |
const | (命令列に即値 1 が直書きされる) | フィールドアクセスなし |
5. ローカル変数との比較サマリ
項目 | ローカル変数 | インスタンス フィールド | static フィールド | const |
---|---|---|---|---|
メモリ | スタック | ヒープ(オブジェクト内) | 静的ヒープ | なし |
IL 命令 | stloc/ldloc | stfld/ldfld | stsfld/ldsfld | ldc.i4.* |
GC の影響 | – | オブジェクトの移動でスライド | AppDomain 単位 (世代 0 には載らない) | – |
ラムダで捕捉 | DisplayClass に昇格 | 変更なし | 変更なし | – |
6. まとめ
- メンバー変数(フィールド)はローカル変数とは配置先が異なり、
- インスタンス フィールドはオブジェクトの一部としてヒープに、
- static フィールドは型単位の静的領域に、
- const フィールドはそもそもメモリを消費せず即値埋め込み――という点が最大の差です。
- IL でも命令セットが異なる (stfld / stsfld / ldc.*) ため、デバッガや ILDASM で確認すると違いが明確に分かります。
- 逆に、値のサイズ (int なら常に 4 byte) や little-endian 順序といったビット表現自体はローカルでもフィールドでも変わりません。
訪問数 7 回, 今日の訪問数 1回
ディスカッション
コメント一覧
まだ、コメントがありません