数组,堆和堆栈和值类型
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个字节来存储它的值。 因此, RefType
和ValType
的值的内存如下所示:
0 ┌───────────────────┐ │ I │ 4 ├───────────────────┤ │ S │ 8 ├───────────────────┤ │ L │ │ │ 16 └───────────────────┘
现在,如果在函数中有三个局部变量,类型为RefType
, ValType
和int[]
,如下所示:
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://www.djcxy.com/p/2225.html