145
int[] myIntegers;
myIntegers = new int[100];

上記のコードでは、new int [100]はヒープ上に配列を生成していますか?私がCLRでc#を介して読んだことから、答えはイエスです。しかし、私が理解できないのは、配列内の実際のintがどうなるかということです。これらは値型であるため、ボックス化する必要があると思います。たとえば、myIntegersをプログラムの他の部分に渡すと、スタックが常に残っているとスタックが乱雑になります。 。それとも私は間違っていますか?それらはボックス化されただけで、アレイが存在する限りヒープ上に存在すると思います。

4

8 に答える 8

311

配列はヒープに割り当てられ、intはボックス化されていません。

参照型はヒープに割り当てられ、値型はスタックに割り当てられると人々が言っ​​ているため、混乱の原因となる可能性があります。これは完全に正確な表現ではありません。

すべてのローカル変数とパラメーターはスタックに割り当てられます。これには、値型と参照型の両方が含まれます。2つの違いは、変数に格納されているものだけです。当然のことながら、値型の場合、型のは変数に直接格納され、参照型の場合、型の値はヒープに格納され、この値への参照が変数に格納されます。

同じことがフィールドにも当てはまります。class集約タイプ(aまたはa )のインスタンスにメモリを割り当てる場合、struct各インスタンスフィールドのストレージを含める必要があります。参照型フィールドの場合、このストレージは値への参照のみを保持し、それ自体は後でヒープに割り当てられます。値型フィールドの場合、このストレージは実際の値を保持します。

したがって、次のタイプが与えられます。

class RefType{
    public int    I;
    public string S;
    public long   L;
}

struct ValType{
    public int    I;
    public string S;
    public long   L;
}

これらの各タイプの値には、16バイトのメモリが必要です(32ビットのワードサイズを想定)。Iいずれの場合も、フィールドは値を格納するのに4バイトかかり、フィールドは参照を格納するのに4バイトかかり、フィールドはSLを格納するのに8バイトかかります。したがって、両方の値のメモリは次のようにRefTypeなりValTypeます。

0┌───────────────────┐
   │私​​│
 4├───────────────────┤
   │S│
 8├───────────────────┤
   │L│
   ││
16└───────────────────┘

ここで、関数に、、、、およびのタイプRefTypeの3つのローカル変数がある場合、次のようになります。ValTypeint[]

RefType refType;
ValType valType;
int[]   intArray;

その場合、スタックは次のようになります。

0┌───────────────────┐
   │refType│
 4├───────────────────┤
   │valType│
   ││
   ││
   ││
20├───────────────────┤
   │intArray│
24└───────────────────┘

次のように、これらのローカル変数に値を割り当てた場合:

refType = new RefType();
refType.I = 100;
refType.S = "refType.S";
refType.L = 0x0123456789ABCDEF;

valType = new ValType();
valType.I = 200;
valType.S = "valType.S";
valType.L = 0x0011223344556677;

intArray = new int[4];
intArray[0] = 300;
intArray[1] = 301;
intArray[2] = 302;
intArray[3] = 303;

その場合、スタックは次のようになります。

0┌───────────────────┐
   │0x4A963B68│-`refType`のヒープアドレス
 4├───────────────────┤
   │200│-`valType.I`の値
   │0x4A984C10│-`valType.S`のヒープアドレス
   │0x44556677│-`valType.L`の下位32ビット
   │0x00112233│-上位32ビットの`valType.L`
20├───────────────────┤
   │0x4AA4C288│-`intArray`のヒープアドレス
24└───────────────────┘

0x4A963B68アドレス(の値)のメモリは次のrefTypeようになります。

0┌───────────────────┐
   │100│-`refType.I`の値
 4├───────────────────┤
   │0x4A984D88│-`refType.S`のヒープアドレス
 8├───────────────────┤
   │0x89ABCDEF│-`refType.L`の下位32ビット
   │0x01234567│-上位32ビットの`refType.L`
16└───────────────────┘

0x4AA4C288アドレス(の値)のメモリは次のintArrayようになります。

0┌───────────────────┐
   │4│-配列の長さ
 4├───────────────────┤
   │300│-`intArray[0]`
 8├───────────────────┤
   │301│-`intArray[1]`
12├───────────────────┤
   │302│-`intArray[2]`
16├───────────────────┤
   │303│-`intArray[3]`
20└───────────────────┘

intArrayこれで、別の関数に渡した場合、スタックにプッシュされる値は、配列のコピーではなく0x4AA4C288、配列のアドレスになります。

于 2009-07-11T17:03:56.113 に答える
24

はい、アレイはヒープ上に配置されます。

配列内のintはボックス化されません。値型がヒープに存在するからといって、必ずしもそれがボックス化されることを意味するわけではありません。ボックス化は、intなどの値型が型オブジェクトの参照に割り当てられている場合にのみ発生します。

例えば

ボックス化しない:

int i = 42;
myIntegers[0] = 42;

ボックス:

object i = 42;
object[] arr = new object[10];  // no boxing here 
arr[0] = 42;

このテーマに関するEricの投稿もチェックしてください。

于 2009-07-11T14:35:40.027 に答える
22

何が起こっているのかを理解するために、ここにいくつかの事実があります:

  • オブジェクトは常にヒープに割り当てられます。
  • ヒープにはオブジェクトのみが含まれます。
  • 値型は、スタックに割り当てられるか、ヒープ上のオブジェクトの一部に割り当てられます。
  • 配列はオブジェクトです。
  • 配列には値型のみを含めることができます。
  • オブジェクト参照は値型です。

したがって、整数の配列がある場合、その配列はヒープに割り当てられ、それに含まれる整数はヒープ上の配列オブジェクトの一部になります。整数は、個別のオブジェクトとしてではなく、ヒープ上の配列オブジェクト内に存在するため、ボックス化されていません。

文字列の配列がある場合、それは実際には文字列参照の配列です。参照は値型であるため、ヒープ上の配列オブジェクトの一部になります。文字列オブジェクトを配列に配置すると、実際には文字列オブジェクトへの参照が配列に配置され、文字列はヒープ上の別個のオブジェクトになります。

于 2009-07-11T15:49:06.963 に答える
11

あなたの質問の核心は、参照型と値型についての誤解だと思います。これはおそらくすべての.NETおよびJava開発者が苦労していることです。

配列は単なる値のリストです。参照型の配列(たとえば、 )の場合、参照は参照型のであるため、配列はヒープ上のstring[]さまざまなオブジェクトへの参照のリストです。内部的には、これらの参照はメモリ内のアドレスへのポインタとして実装されます。これを視覚化したい場合、そのような配列はメモリ内(ヒープ上)で次のようになります。string

[ 00000000, 00000000, 00000000, F8AB56AA ]

これは、ヒープ上のオブジェクトへのstring4つの参照を含む配列です(ここでの数値は16進数です)。string現在、最後のものだけがstring実際に何かを指しています(割り当てられるとメモリはすべてゼロに初期化されます)、この配列は基本的にC#のこのコードの結果になります:

string[] strings = new string[4];
strings[3] = "something"; // the string was allocated at 0xF8AB56AA by the CLR

上記の配列は32ビットプログラムになります。64ビットプログラムでは、参照は2倍の大きさになります(F8AB56AAになります00000000F8AB56AA)。

値型の配列(たとえば)がある場合、値型の値自体(したがって名前)であるためint[]、配列は整数のリストです。このような配列の視覚化は次のようになります。

[ 00000000, 45FF32BB, 00000000, 00000000 ]

これは4つの整数の配列であり、2番目のintのみに値(1174352571、つまりその16進数の10進表現)が割り当てられ、残りの整数は0になります(前述のように、メモリはゼロに初期化されます) 16進数の00000000は10進数で0です)。この配列を生成したコードは次のようになります。

 int[] integers = new int[4];
 integers[1] = 1174352571; // integers[1] = 0x45FF32BB would be valid too

このint[]配列もヒープに格納されます。

別の例として、short[4]配列のメモリは次のようになります。

[ 0000, 0000, 0000, 0000 ]

aのshortは2バイトの数値であるため。

値型が格納されている場所は、Eric Lippertがここで非常によく説明しているように、実装の詳細であり、値型と参照型の違い(動作の違い)に固有のものではありません。

メソッドに何かを渡すと(参照型または値型)、その型の値のコピーが実際にメソッドに渡されます。参照型の場合、は参照であり(これはメモリの一部へのポインタと考えてください。ただし、これは実装の詳細でもあります)、値型の場合、値はそれ自体です。

// Calling this method creates a copy of the *reference* to the string
// and a copy of the int itself, so copies of the *values*
void SomeMethod(string s, int i){}

ボックス化は、値型を参照型に変換する場合にのみ発生します。このコードボックス:

object o = 5;
于 2009-07-11T16:57:11.243 に答える
3

これらは@PDaddyによる上記の回答を描いたイラストです

ここに画像の説明を入力してください

ここに画像の説明を入力してください

そして、対応する内容を自分のスタイルで描きました。

ここに画像の説明を入力してください

于 2017-12-06T01:00:11.257 に答える
2

誰もが十分に言っていますが、ヒープ、スタック、ローカル変数、静的変数に関する明確な(ただし非公式の)サンプルとドキュメントを探している場合は、.NETのメモリに関するJonSkeetの完全な記事を参照してください。どこ

抜粋:

  1. 各ローカル変数(つまり、メソッドで宣言された変数)はスタックに格納されます。これには参照型変数が含まれます。変数自体はスタック上にありますが、参照型変数の値は参照(またはnull)であり、オブジェクト自体ではないことに注意してください。メソッドパラメータもローカル変数としてカウントされますが、ref修飾子で宣言されている場合、独自のスロットは取得されませんが、呼び出し元のコードで使用されている変数とスロットを共有します。詳細については、パラメーターの受け渡しに関する私の記事を参照してください。

  2. 参照型のインスタンス変数は常にヒープ上にあります。ここに、オブジェクト自体が「存在」します。

  3. 値型のインスタンス変数は、値型を宣言する変数と同じコンテキストに格納されます。インスタンスのメモリスロットには、インスタンス内の各フィールドのスロットが効果的に含まれています。つまり、(前の2つのポイントを前提として)メソッド内で宣言された構造体変数は常にスタック上にあり、クラスのインスタンスフィールドである構造体変数はヒープ上にあります。

  4. すべての静的変数は、参照型または値型のどちらで宣言されているかに関係なく、ヒープに格納されます。作成されるインスタンスの数に関係なく、合計で1つのスロットしかありません。(ただし、その1つのスロットが存在するためにインスタンスを作成する必要はありません。)変数が存在する正確なヒープの詳細は複雑ですが、このテーマに関するMSDNの記事で詳細に説明されています。

于 2013-04-09T07:43:44.233 に答える
1

整数の配列がヒープに割り当てられます。それ以上でもそれ以下でもありません。myIntegersは、intが割り当てられているセクションの先頭を参照します。その参照はスタックにあります。

オブジェクトタイプのような参照型オブジェクトの配列がある場合、スタック上にあるmyObjects []は、オブジェクト自体を参照する一連の値を参照します。

要約すると、myIntegersを一部の関数に渡す場合は、実際の整数の束が割り当てられている場所への参照のみを渡します。

于 2009-07-11T14:38:02.813 に答える
1

サンプルコードにはボクシングはありません。

値型は、intの配列と同じようにヒープ上に存在できます。配列はヒープに割り当てられ、値型であるintを格納します。配列の内容はdefault(int)に初期化されますが、これはたまたまゼロです。

値型を含むクラスについて考えてみます。


    class HasAnInt
    {
        int i;
    }

    HasAnInt h = new HasAnInt();

変数hは、ヒープ上に存在するHasAnIntのインスタンスを指します。たまたま値型が含まれています。それは完全に大丈夫です、「私」はクラスに含まれているので、たまたまヒープ上に住んでいます。この例にもボクシングはありません。

于 2009-07-11T15:07:46.277 に答える