数组、堆和堆栈以及值类型 [英] Arrays, heap and stack and value types

查看:46
本文介绍了数组、堆和堆栈以及值类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

在上面的代码中,new int[100]是在堆上生成数组吗?从我通过 c# 在 CLR 上阅读的内容来看,答案是肯定的.但我无法理解的是,数组内的实际 int 会发生什么.由于它们是值类型,我猜它们必须被装箱,例如,因为我可以将 myIntegers 传递给程序的其他部分,如果它们一直留在堆栈上,它会弄乱堆栈.还是我错了?我猜他们只是被装箱,只要数组存在,它们就会一直存在于堆中.

解决方案

你的数组是在堆上分配的,整数没有装箱.

你困惑的根源可能是因为人们说引用类型是在堆上分配的,而值类型是在堆栈上分配的.这并不完全准确.

所有的局部变量和参数都在栈上分配.这包括值类型和引用类型.两者的区别仅在于存储在变量中的内容.不出所料,对于一个值类型,该类型的直接存储在变量中,而对于一个引用类型,该类型的值存储在堆上,一个引用 这个值就是存储在变量中的内容.

字段也是如此.当为聚合类型的实例(classstruct)分配内存时,它必须包括其每个实例字段的存储空间.对于引用类型的字段,此存储仅保存对该值的引用,该值本身稍后将在堆上分配.对于值类型字段,此存储保存实际值.

所以,给定以下类型:

class RefType{公共国际我;公共字符串 S;公长L;}结构值类型{公共国际我;公共字符串 S;公长L;}

这些类型中的每一种的值都需要 16 字节的内存(假设字长为 32 位).在每种情况下,字段 I 需要 4 个字节来存储其值,字段 S 需要 4 个字节来存储其引用,而字段 L需要 8 个字节来存储它的值.所以 RefTypeValType 的值的内存看起来像这样:

<前>0 ┌───────────────────────┐│ 我 │4 ├─────────────────────┤│ S │8 ├─────────────────────┤│ L ││ │16 └────────────────────────┘

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

RefType refType;ValType valType;int[] intArray;

那么您的堆栈可能如下所示:

<前>0 ┌───────────────────────┐│ refType │4 ├─────────────────────┤│ 值类型 ││ ││ ││ │20 ├─────────────────────┤│ intArray │24 └────────────────────────┘

如果你给这些局部变量赋值,像这样:

refType = new RefType();refType.I = 100;refType.S = "refType.S";refType.L = 0x0123456789ABCDEF;valType = 新的 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 │ -- `valType.L` 的高 32 位20 ├─────────────────────┤│ 0x4AA4C288 │ -- `intArray`的堆地址24 └────────────────────────┘

地址 0x4A963B68 处的内存(refType 的值)类似于:

<前>0 ┌───────────────────────┐│ 100 │ -- `refType.I` 的值4 ├─────────────────────┤│ 0x4A984D88 │ -- `refType.S`的堆地址8 ├─────────────────────┤│ 0x89ABCDEF │ -- `refType.L` 的低 32 位│ 0x01234567 │ -- `refType.L` 的高 32 位16 └────────────────────────┘

地址 0x4AA4C288 处的内存(intArray 的值)类似于:

<前>0 ┌───────────────────────┐│ 4 │ -- 数组长度4 ├─────────────────────┤│ 300 │ -- `intArray[0]`8 ├─────────────────────┤│ 301 │ -- `intArray[1]`12 ├─────────────────────┤│ 302 │ -- `intArray[2]`16 ├─────────────────────┤│ 303 │ -- `intArray[3]`20 └────────────────────────┘

现在,如果你将 intArray 传递给另一个函数,压入堆栈的值将是 0x4AA4C288,数组的地址,不是 数组的副本.

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

In the above code, is new int[100] generating the array on the heap? From what I've read on CLR via c#, the answer is yes. But what I can't understand, is what happens to the actual int's inside the array. As they are value types, I'd guess they'd have to be boxed, as I can, for example, pass myIntegers to other parts of the program and it'd clutter up the stack if they were left on it all the time. Or am I wrong? I'd guess they'd just be boxed and would live on the heap for as long the array existed.

解决方案

Your array is allocated on the heap, and the ints are not boxed.

The source of your confusion is likely because people have said that reference types are allocated on the heap, and value types are allocated on the stack. This is not an entirely accurate representation.

All local variables and parameters are allocated on the stack. This includes both value types and reference types. The difference between the two is only what is stored in the variable. Unsurprisingly, for a value type, the value of the type is stored directly in the variable, and for a reference type, the value of the type is stored on the heap, and a reference to this value is what is stored in the variable.

The same holds for fields. When memory is allocated for an instance of an aggregate type (a class or a struct), it must include storage for each of its instance fields. For reference-type fields, this storage holds just a reference to the value, which would itself be allocated on the heap later. For value-type fields, this storage holds the actual value.

So, given the following types:

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

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

The values of each of these types would require 16 bytes of memory (assuming a 32-bit word size). The field I in each case takes 4 bytes to store its value, the field S takes 4 bytes to store its reference, and the field L takes 8 bytes to store its value. So the memory for the value of both RefType and ValType looks like this:

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

Now if you had three local variables in a function, of types RefType, ValType, and int[], like this:

RefType refType;
ValType valType;
int[]   intArray;

then your stack might look like this:

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

If you assigned values to these local variables, like so:

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;

Then your stack might look something like this:

 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 └───────────────────┘

Memory at address 0x4A963B68 (value of refType) would be something like:

 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 └───────────────────┘

Memory at address 0x4AA4C288 (value of intArray) would be something like:

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

Now, if you passed intArray to another function, the value pushed onto the stack would be 0x4AA4C288, the address of the array, not a copy of the array.

这篇关于数组、堆和堆栈以及值类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆