数组,堆和堆栈和值类型

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

在上面的代码中,是新的int [100]在堆上生成数组? 从我通过C#阅读的CLR中,答案是肯定的。 但是我无法理解的是,数组内部的实际int会发生什么。 因为它们是值类型,所以我猜想他们必须被装箱,例如,将myInteger传递给程序的其他部分,并且如果它始终保留在堆栈上,它会混乱堆栈。 或者我错了? 我想他们只会被装箱,并且只要阵列存在就会活在堆上。


你的数组被分配在堆上,并且整数没有被装箱。

混淆的根源很可能是因为人们说引用类型被分配到堆上,而值类型被分配到堆栈上。 这不是一个完全准确的表示。

所有本地变量和参数都分配在堆栈上。 这包括值类型和参考类型。 两者之间的差别只是存储在变量中的内容。 不出所料,对于一个值类型,类型的值直接存储在变量中,对于引用类型,类型的值存储在堆中,并且对该值的引用是存储在变量中的值。

领域也是如此。 当为聚合类型(类或结构)的实例分配内存时,它必须包含每个实例字段的存储。 对于引用类型的字段,这个存储只保存对该值的引用,该值本身将在堆中分配。 对于值类型字段,此存储保存实际值。

所以,给定以下几种类型:

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个字节来存储它的值,字段S需要4个字节来存储它的引用,并且字段L需要8个字节来存储它的值。 因此, RefTypeValType的值的内存如下所示:

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

现在,如果在函数中有三个局部变量,类型为RefTypeValTypeint[] ,如下所示:

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     │ -- heap address of `refType`
 4 ├───────────────────┤
   │       200         │ -- value of `valType.I`
   │    0x4A984C10     │ -- heap address of `valType.S`
   │    0x44556677     │ -- low 32-bits of `valType.L`
   │    0x00112233     │ -- high 32-bits of `valType.L`
20 ├───────────────────┤
   │    0x4AA4C288     │ -- heap address of `intArray`
24 └───────────────────┘

地址为0x4A963B68( refType值)的内存将如下所示:

 0 ┌───────────────────┐
   │       100         │ -- value of `refType.I`
 4 ├───────────────────┤
   │    0x4A984D88     │ -- heap address of `refType.S`
 8 ├───────────────────┤
   │    0x89ABCDEF     │ -- low 32-bits of `refType.L`
   │    0x01234567     │ -- high 32-bits of `refType.L`
16 └───────────────────┘

地址为0x4AA4C288(intArray的值)的intArray将如下所示:

 0 ┌───────────────────┐
   │        4          │ -- length of array
 4 ├───────────────────┤
   │       300         │ -- `intArray[0]`
 8 ├───────────────────┤
   │       301         │ -- `intArray[1]`
12 ├───────────────────┤
   │       302         │ -- `intArray[2]`
16 ├───────────────────┤
   │       303         │ -- `intArray[3]`
20 └───────────────────┘

现在,如果将intArray传递给另一个函数,则推入堆栈的值将为0x4AA4C288,即数组的地址, 而不是数组的副本。


是的,数组将位于堆上。

阵列中的整数不会被装箱。 仅仅因为堆中存在值类型,并不一定意味着它会被装箱。 只有在将值类型(如int)分配给类型对象的引用时,才会发生装箱。

例如

不包括:

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

盒:

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

您可能还想看看Eric的这个主题的帖子:

  • http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx

  • 为了理解发生了什么,下面是一些事实:

  • 对象始终分配在堆上。
  • 堆只包含对象。
  • 值类型既可以在堆栈中分配,也可以分配给堆中的对象的一部分。
  • 一个数组是一个对象。
  • 数组只能包含值类型。
  • 对象引用是一种值类型。
  • 因此,如果您有一个整数数组,则该数组将被分配到堆中,并且它所包含的整数是堆中数组对象的一部分。 整数位于堆中的数组对象内部,而不是单独的对象,因此它们不是装箱的。

    如果你有一个字符串数组,它实际上是一个字符串引用数组。 作为引用是值类型,它们将成为堆上数组对象的一部分。 如果将一个字符串对象放在数组中,实际上将字符串对象的引用放入数组中,并且该字符串是堆中单独的对象。

    链接地址: http://www.djcxy.com/p/2225.html

    上一篇: Arrays, heap and stack and value types

    下一篇: Difference between static class and singleton pattern?