当operator new()
与引用类型一起使用时,实例的空间在堆上分配,引用变量本身放在堆栈上。除此之外,在堆上分配的引用类型实例中的所有内容都将被清零
例如,这是一个类:
class Person
{
public int id;
public string name;
}
在以下代码中:
class PersonDemo
{
static void Main()
{
Person p = new Person();
Console.WriteLine("id: {0} name: {1}", p.id, p.name);
}
}
p
变量在堆栈上,并且创建的Person
实例(所有它的成员)都在堆上。 p.id
为0
,p.name
为null
。
这就是这种情况,因为在堆上分配的所有内容都已清零。
现在我感到困惑的是,如果我使用的是new
运算符的值类型。例如,考虑以下结构:
struct Date
{
public int year;
public int month;
public int day;
}
class DateDemo
{
static void Main()
{
Date someDate;
someDate= new Date();
Console.WriteLine("someDate is: {0}/{1}/{2}",
someDate.month, someDate.day, someDate.year);
}
}
现在我想知道main的以下几行:
Date someDate;
someDate= new Date();
在第一行someDate
变量在堆栈上分配。准确地说是12个字节
我的问题是第二行会发生什么? operator new()
做了什么?它是否仅将Date
结构的成员清空,或者它还在堆上分配空间?一方面我不希望new
在堆上分配空间,当然因为在第一行中已经在堆栈中为结构实例分配了内存。另一方面,我希望new
在堆上分配空间并返回该空间的地址,因为这是new
应该做的事情。
也许这是因为我来自C ++背景。
然而,如果答案是:“当new
与值类型一起使用时,它只会将对象的成员清零”,而不是new
运算符的含义不一致,因为:
new
与值类型一起使用时,它只会将堆栈中对象的成员清零new
与引用类型一起使用时,它会在堆上为实例分配内存并使其成员为zerous-out 提前致谢,
干杯
答案 0 :(得分:20)
答案 1 :(得分:5)
好的,这是一个简单的:
class Program
{
static void Main(string[] args)
{
DateTime dateTime = new DateTime();
dateTime = new DateTime();
Console.Read();
}
}
编译到这个IL代码:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 24 (0x18)
.maxstack 1
.locals init ([0] valuetype [mscorlib]System.DateTime dateTime)
IL_0000: nop
IL_0001: ldloca.s dateTime
IL_0003: initobj [mscorlib]System.DateTime
IL_0009: ldloca.s dateTime
IL_000b: initobj [mscorlib]System.DateTime
IL_0011: call int32 [mscorlib]System.Console::Read()
IL_0016: pop
IL_0017: ret
} // end of method Program::Main
正如您所看到的,CLR将使用相同的局部变量来存储新的值类型,尽管它将再次运行构造函数 - ,这很可能只是将内存归零。我们无法看到initobj
是什么,这是 CLR实施。
现实是,正如Eric Lippert解释here,没有关于在堆栈上分配的值类型的一般规则。这完全取决于CLR的实现。
答案 2 :(得分:2)
struct的默认构造函数返回一个结构,其中所有内存都归零。也就是说,new SomeStruct()
与default(SomeStruct)
相同。
然后,您的代码会将该默认结构分配给您的变量。
这就是你所知道的。
编译器如何实现这一点完全是编译器业务。
但是如果你对幕后很好奇,编译器很可能只是直接清除该变量的堆栈位置:假设变量存储在堆栈中。有许多事情可以阻止这种情况 - 一个例子是访问它的匿名函数,即:
Func<Person> PersonFactory()
{
Person p = new Person();
return () => p;
}
这里p需要存储在堆上才能在函数返回等时存在,因此new Person()
将清除该堆位置。
反正。与C / C ++不同,使用C#,忘记“堆栈”,“堆”等是一个好主意.AFAIK,语言规范没有任何一个概念 - 它们都是特定的实现。谁知道,在逃避分析允许的情况下,某些未来的实现可能会在堆栈上放置一些堆值以节省GC一些工作量。最好不要针对特定的C#规范实现做出设计决策。
答案 3 :(得分:1)
从开发人员的角度来看,您不知道它的分配位置。例如,具有CLR的奇异设备,其不知道堆栈 - &gt; everthing在堆上。即使您考虑桌面CLR,在某些情况下JITer也可以将变量从堆栈移动到堆中。
答案 4 :(得分:0)
关于结构的归零。
无参数构造函数将成员归零。
如果您不使用new()
,则无法访问struct成员,除非您首先自行初始化它们。否则你将获得“使用可能未分配的字段”。