C# 数组、堆、堆栈和值类型
在上面的代码中,new int[100]是否在堆上生成数组?从我通过c#在CLR上读到的内容来看,答案是肯定的。但我不明白的是,数组中实际的int发生了什么。由于它们是值类型,我猜它们必须被装箱,例如,我可以将MyInteger传递给程序的其他部分,如果它们一直留在堆栈上,那么堆栈就会变得杂乱无章。还是我错了?我猜它们只是被装箱了,只要数组存在,它们就会在堆上生存。是的,数组将位于堆上 数组中的整数将不会装箱。仅仅因为堆上存在值类型,并不一定意味着它将被装箱。只有将值类型(如int)指定给object类型的引用时,才会发生装箱 比如说 不包含以下框:C# 数组、堆、堆栈和值类型,c#,arrays,memory,stack,heap,C#,Arrays,Memory,Stack,Heap,在上面的代码中,new int[100]是否在堆上生成数组?从我通过c#在CLR上读到的内容来看,答案是肯定的。但我不明白的是,数组中实际的int发生了什么。由于它们是值类型,我猜它们必须被装箱,例如,我可以将MyInteger传递给程序的其他部分,如果它们一直留在堆栈上,那么堆栈就会变得杂乱无章。还是我错了?我猜它们只是被装箱了,只要数组存在,它们就会在堆上生存。是的,数组将位于堆上 数组中的整数将不会装箱。仅仅因为堆上存在值类型,并不一定意味着它将被装箱。只有将值类型(如int)指定给obj
int[] myIntegers;
myIntegers = new int[100];
方框:
int i = 42;
myIntegers[0] = 42;
您还可以查看Eric关于此主题的帖子:
总之,如果将MyInteger传递给某些函数,则只传递对实际整数组分配位置的引用。示例代码中没有装箱 值类型可以像在int数组中一样存在于堆中。数组是在堆上分配的,它存储int,而int恰好是值类型。数组的内容被初始化为默认值(int),该值恰好为零 考虑一个包含值类型的类:
object i = 42;
object[] arr = new object[10]; // no boxing here
arr[0] = 42;
变量h指的是堆上的hasaint实例。它恰好包含一个值类型。这完全没关系,'我'正好住在堆上,因为它包含在一个类中。本例中也没有装箱。为了了解发生了什么,以下是一些事实:
- 对象始终在堆上分配
- 堆只包含对象
- 值类型要么在堆栈上分配,要么在堆上分配对象的一部分
- 数组是一个对象
- 数组只能包含值类型
- 对象引用是一种值类型
如果您有一个字符串数组,那么它实际上是一个字符串引用数组。由于引用是值类型,它们将是堆上数组对象的一部分。如果在数组中放入字符串对象,实际上就是在数组中放入对字符串对象的引用,并且字符串是堆上的一个单独对象。我认为您问题的核心在于对引用和值类型的误解。这可能是每个.NET和Java开发人员都在努力解决的问题 数组只是一个值列表。如果它是引用类型的数组(例如
string[]
),那么该数组就是堆上各种string
对象的引用列表,因为引用是引用类型的值。在内部,这些引用被实现为指向内存中地址的指针。如果希望将其可视化,这样的数组在内存中(在堆上)将如下所示:
[00000000,00000000,00000000,F8AB56AA]
这是一个string
数组,其中包含对堆上string
对象的4个引用(这里的数字是十六进制的)。目前,只有最后一个字符串实际指向任何东西(内存在分配时初始化为所有零),此数组基本上是C#中此代码的结果:
上面的数组将在一个32位程序中。在64位程序中,引用将是两倍大(F8AB56AA
将是00000000 F8AB56AA
)
如果您有一个值类型数组(比如一个int[]
),那么该数组就是一个整数列表,因为值类型的值就是值本身(因此而得名)。这种阵列的可视化如下:
[00000000,45FF32BB,00000000,00000000]
这是一个由4个整数组成的数组,其中只有第二个整数被赋值(到1174352571,这是该十六进制数的十进制表示),其余整数将为0(如我所说,内存初始化为零,十六进制的00000000是十进制的0)。生成此数组的代码为:
string[] strings = new string[4];
strings[3] = "something"; // the string was allocated at 0xF8AB56AA by the CLR
这个int[]
数组也将存储在堆上
另一个例子是,short[4]
数组的内存如下所示:
class RefType{
public int I;
public string S;
public long L;
}
struct ValType{
public int I;
public string S;
public long L;
}
0 ┌───────────────────┐
│ I │
4 ├───────────────────┤
│ S │
8 ├───────────────────┤
│ L │
│ │
16 └───────────────────┘
RefType refType;
ValType valType;
int[] intArray;
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;
[0000,0000,0000,0000]
因为short
的值是一个2字节的数字
存储值类型的位置只是一个实现细节,正如Eric Lippert很好地解释的那样,不是值类型和引用类型之间固有的差异(即行为上的差异)
当您将某个内容传递给某个方法(引用类型或值类型)时,该类型的值的副本实际上会传递给该方法。在引用类型的情况下,值是一个引用(将其视为指向一段内存的指针,尽管这也是一个实现细节),而在值类型的情况下,值就是对象本身
int[] integers = new int[4];
integers[1] = 1174352571; // integers[1] = 0x45FF32BB would be valid too
只有将值类型转换为引用类型时,才会发生装箱。此代码框:
// 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){}
您的数组是在堆上分配的,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 └───────────────────┘
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 └───────────────────┘
0 ┌───────────────────┐
│ 4 │ -- length of array
4 ├───────────────────┤
│ 300 │ -- `intArray[0]`
8 ├───────────────────┤
│ 301 │ -- `intArray[1]`
12 ├───────────────────┤
│ 302 │ -- `intArray[2]`
16 ├───────────────────┤
│ 303 │ -- `intArray[3]`
20 └───────────────────┘