[解決済み】構造体の "new "は、ヒープやスタックに割り当てるのですか?
質問
を使用してクラスのインスタンスを作成する場合、そのインスタンスは
new
演算子を使用すると、ヒープ上にメモリが確保されます。構造体のインスタンスを
new
演算子は、ヒープやスタックのどこにメモリを確保するのですか?
どのように解決するのですか?
では、もう少し分かりやすく説明しましょう。
まず、アッシュの言う通り、問題は
ではなく
を使用することができます。
変数
が割り当てられています。これは別の問題で、答えは単に「スタック上」だけではありません。C#2では、さらに複雑になっています。私は
このトピックに関する記事
のみであり、要望があればさらに詳しく説明しますが、ここでは
new
演算子を使用します。
第二に、これらのすべては、あなたがどのレベルについて話しているのかに本当に依存します。私は、コンパイラがソースコードに対して何をするのか、ILを作成するという観点から見ています。JITコンパイラは、多くの論理的アロケーションを最適化するような巧妙なことを行う可能性があります。
第三に、ジェネリックを無視するのは、実は答えを知らないからであり、物事を複雑にしすぎるからでもある。
最後に、これらはすべて、現在の実装での話です。C#の仕様ではあまり規定されておらず、事実上、実装のディテールとなっています。マネージドコードの開発者は本当に気にする必要はないと考える人もいます。しかし、実際にすべてのローカル変数がヒープ上に存在する世界を想像してみると、やはり仕様に準拠することになるでしょう。
には2種類の状況があります。
new
演算子は、パラメータなしのコンストラクタを呼び出すことができます (たとえば
new Guid()
) またはパラメータ付きコンストラクタ (例.
new Guid(someString)
). これらは大幅に異なるILを生成します。その理由を理解するには、C#とCLIの仕様を比較する必要があります。C#によると、すべての値型はパラメータなしのコンストラクタを持っています。CLI仕様によると
いいえ
の値型はパラメータレスコンストラクタを持ちます。(いつかリフレクションで値型のコンストラクタを取得してみてください。パラメータなしのものは見つからないでしょう。)
C# がゼロで値を初期化することをコンストラクタとして扱うのは、言語の一貫性を保つために理にかなっています。
new(...)
として
常に
はコンストラクタを呼び出します。CLI では、呼び出す実際のコードがないため、また型固有のコードもないため、別の方法で考えるのが理にかなっています。
また、初期化した後の値をどうするかも違います。に使われるILは
Guid localVariable = new Guid(someString);
に使用されるILとは異なります。
myInstanceOrStaticVariable = new Guid(someString);
また、メソッド呼び出しの引数など、中間値として使われる場合は、また少し事情が違ってきます。これらの違いを示すために、ここに短いテストプログラムがあります。これは、スタティック変数とインスタンス変数の違いを示すものではありません。
stfld
と
stsfld
が、それだけです。
using System;
public class Test
{
static Guid field;
static void Main() {}
static void MethodTakingGuid(Guid guid) {}
static void ParameterisedCtorAssignToField()
{
field = new Guid("");
}
static void ParameterisedCtorAssignToLocal()
{
Guid local = new Guid("");
// Force the value to be used
local.ToString();
}
static void ParameterisedCtorCallMethod()
{
MethodTakingGuid(new Guid(""));
}
static void ParameterlessCtorAssignToField()
{
field = new Guid();
}
static void ParameterlessCtorAssignToLocal()
{
Guid local = new Guid();
// Force the value to be used
local.ToString();
}
static void ParameterlessCtorCallMethod()
{
MethodTakingGuid(new Guid());
}
}
以下は、無関係なビット(nopsなど)を除いたクラスのILです。
.class public auto ansi beforefieldinit Test extends [mscorlib]System.Object
{
// Removed Test's constructor, Main, and MethodTakingGuid.
.method private hidebysig static void ParameterisedCtorAssignToField() cil managed
{
.maxstack 8
L_0001: ldstr ""
L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
L_000b: stsfld valuetype [mscorlib]System.Guid Test::field
L_0010: ret
}
.method private hidebysig static void ParameterisedCtorAssignToLocal() cil managed
{
.maxstack 2
.locals init ([0] valuetype [mscorlib]System.Guid guid)
L_0001: ldloca.s guid
L_0003: ldstr ""
L_0008: call instance void [mscorlib]System.Guid::.ctor(string)
// Removed ToString() call
L_001c: ret
}
.method private hidebysig static void ParameterisedCtorCallMethod() cil managed
{
.maxstack 8
L_0001: ldstr ""
L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
L_000b: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid)
L_0011: ret
}
.method private hidebysig static void ParameterlessCtorAssignToField() cil managed
{
.maxstack 8
L_0001: ldsflda valuetype [mscorlib]System.Guid Test::field
L_0006: initobj [mscorlib]System.Guid
L_000c: ret
}
.method private hidebysig static void ParameterlessCtorAssignToLocal() cil managed
{
.maxstack 1
.locals init ([0] valuetype [mscorlib]System.Guid guid)
L_0001: ldloca.s guid
L_0003: initobj [mscorlib]System.Guid
// Removed ToString() call
L_0017: ret
}
.method private hidebysig static void ParameterlessCtorCallMethod() cil managed
{
.maxstack 1
.locals init ([0] valuetype [mscorlib]System.Guid guid)
L_0001: ldloca.s guid
L_0003: initobj [mscorlib]System.Guid
L_0009: ldloc.0
L_000a: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid)
L_0010: ret
}
.field private static valuetype [mscorlib]System.Guid field
}
ご覧のように、コンストラクタを呼び出すためのさまざまな命令があります。
-
newobj
: スタック上に値を確保し、パラメータ化されたコンストラクタを呼び出します。フィールドへの代入やメソッドの引数として使用するなど、中間的な値に使用されます。 -
call instance
: すでに割り当てられている記憶場所を使用する(スタック上かどうかにかかわらず)。上記のコードでは、ローカル変数への代入に使用されています。同じローカル変数に何度も値を代入する場合、複数のnew
の呼び出しは、古い値の上にデータを初期化するだけです。 はしない。 スタック領域を毎回確保する。 -
initobj
: すでに割り当てられているストレージを使用し、データを消去するだけです。これは、ローカル変数に代入するものも含め、すべてのパラメータなしコンストラクタ呼び出しに使用されます。メソッド呼び出しでは、中間的なローカル変数が効果的に導入され、その値はinitobj
.
このように、いかに複雑なテーマであるかを示すと同時に、少しでも光を当てることができればと思います。で
いくつか
概念的な意味では、すべての
new
しかし、これまで見てきたように、ILレベルでも実際にはそのようなことは起こりません。ある特別なケースを取り上げたいと思います。このメソッドを見てみましょう。
void HowManyStackAllocations()
{
Guid guid = new Guid();
// [...] Use guid
guid = new Guid(someBytes);
// [...] Use guid
guid = new Guid(someString);
// [...] Use guid
}
この場合、論理的には4つのスタックが確保されます。
new
しかし、実際には(この特定のコードでは)スタックは一度だけ割り当てられ、その後同じ記憶場所が再利用されます。
EDIT: 念のため言っておきますが、これは一部のケースにのみ当てはまります。
guid
は表示されません。
Guid
コンストラクタが例外を投げるので、C#コンパイラは同じスタックスロットを再利用できるのです。Eric Lippertの
ブログ記事
の詳細と、その事例として
はありません。
が適用されます。
この回答を書くにあたり、多くのことを学びました。もし不明な点があれば、説明を求めてください
関連
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み】Unity 「関連するスクリプトを読み込むことができません」「Win32Exception: システムは指定されたファイルを見つけることができません"
-
[解決済み】Linq 構文 - 複数列の選択
-
[解決済み] usingディレクティブはネームスペースの内側と外側のどちらを使うべきですか?
-
[解決済み] IDisposable インターフェースの正しい使用法
-
[解決済み] なぜList<T>を継承しないのですか?
-
[解決済み] Typeから新しいオブジェクトのインスタンスを作成する方法
-
[解決済み】なぜC++プログラマは'new'の使用を最小限に抑えなければならないのでしょうか?
-
[解決済み] サブクラスへのポインタのdeleteは、ベースクラスのデストラクタを呼び出しますか?
-
[解決済み] OutOfMemoryExceptionが発生した場合、.NETフレームワークはどのようにメモリを確保するのですか?
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】指定されたキャストが有効でない?
-
[解決済み】SmtpException: トランスポート接続からデータを読み取れません:net_io_connectionclosed
-
[解決済み】WPFでXamlファイルにコメントを追加する方法は?
-
[解決済み】Socket.Selectがエラー "An operation was attempted on something that is not a socket" を返す。
-
[解決済み】値をNULLにすることはできません。パラメータ名:source
-
[解決済み】URLから画像をダウンロードする方法
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
[解決済み】別のスレッドがこのオブジェクトを所有しているため、呼び出し側のスレッドはこのオブジェクトにアクセスできない
-
[解決済み】クラスではなく、構造体を使用する必要があるのはどんな場合ですか?
-
[解決済み】.NETで構造体のデフォルトコンストラクタを定義できないのはなぜですか?