Does using “new” on a struct allocate it on the heap or stack?
使用
好吧,让我们看看我能不能说得更清楚些。好的。
首先,ash是正确的:问题不在于在哪里分配值类型变量。这是一个不同的问题,答案不仅仅是"在堆栈上"。它比这更复杂(而且被C 2弄得更复杂)。我有一篇关于这个主题的文章,如果需要,我将对此进行扩展,但是我们只讨论
第二,所有这些都取决于你所说的水平。我在看编译器如何处理源代码,就它创建的IL而言。在优化掉大量的"逻辑"分配方面,JIT编译器将很有可能做一些聪明的事情。好的。
第三,我忽略了仿制药,主要是因为我不知道答案,部分是因为它会使事情变得太复杂。好的。
最后,所有这些都只是当前的实现。C规范并没有详细说明这方面的内容——它实际上是一个实现细节。有些人认为托管代码开发人员真的不应该关心。我不确定我会走那么远,但有必要想象一个世界,实际上所有局部变量都生活在堆中——这仍然符合规范。好的。
值类型上的
C将"用零初始化一个值"作为构造函数是有意义的,因为它保持了语言的一致性-您可以将
初始化值之后,对它的处理也会有所不同。IL用于好的。
1 |
与用于以下目的的IL不同:好的。
1 |
此外,如果该值用作中间值,例如方法调用的参数,则情况会再次略有不同。为了显示所有这些差异,这里有一个简短的测试程序。它没有显示静态变量和实例变量之间的区别:IL在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | using System; public class Test { static Guid field; static void Main() {} static void MethodTakingGuid(Guid guid) {} static void ParameterisedCtorAssignToField() { field = new Guid(""); } static void ParameterisedCtorAssignToLocal() { Guid local = new Guid(""); // Force the value to be used local.ToString(); } static void ParameterisedCtorCallMethod() { MethodTakingGuid(new Guid("")); } static void ParameterlessCtorAssignToField() { field = new Guid(); } static void ParameterlessCtorAssignToLocal() { Guid local = new Guid(); // Force the value to be used local.ToString(); } static void ParameterlessCtorCallMethod() { MethodTakingGuid(new Guid()); } } |
这是该类的IL,不包括不相关的位(如nops):好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | .class public auto ansi beforefieldinit Test extends [mscorlib]System.Object { // Removed Test's constructor, Main, and MethodTakingGuid. .method private hidebysig static void ParameterisedCtorAssignToField() cil managed { .maxstack 8 L_0001: ldstr"" L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string) L_000b: stsfld valuetype [mscorlib]System.Guid Test::field L_0010: ret } .method private hidebysig static void ParameterisedCtorAssignToLocal() cil managed { .maxstack 2 .locals init ([0] valuetype [mscorlib]System.Guid guid) L_0001: ldloca.s guid L_0003: ldstr"" L_0008: call instance void [mscorlib]System.Guid::.ctor(string) // Removed ToString() call L_001c: ret } .method private hidebysig static void ParameterisedCtorCallMethod() cil managed { .maxstack 8 L_0001: ldstr"" L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string) L_000b: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid) L_0011: ret } .method private hidebysig static void ParameterlessCtorAssignToField() cil managed { .maxstack 8 L_0001: ldsflda valuetype [mscorlib]System.Guid Test::field L_0006: initobj [mscorlib]System.Guid L_000c: ret } .method private hidebysig static void ParameterlessCtorAssignToLocal() cil managed { .maxstack 1 .locals init ([0] valuetype [mscorlib]System.Guid guid) L_0001: ldloca.s guid L_0003: initobj [mscorlib]System.Guid // Removed ToString() call L_0017: ret } .method private hidebysig static void ParameterlessCtorCallMethod() cil managed { .maxstack 1 .locals init ([0] valuetype [mscorlib]System.Guid guid) L_0001: ldloca.s guid L_0003: initobj [mscorlib]System.Guid L_0009: ldloc.0 L_000a: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid) L_0010: ret } .field private static valuetype [mscorlib]System.Guid field } |
如您所见,调用构造函数时使用了许多不同的指令:好的。
- EDOCX1 On the Stack,calls a parametersed constructor.Used for intermediate values,e.g.for assignment to a field or use as a method argument.
- EDOCX1:Uses an already-allocated storage location(whether on the stack or not).这是用于指定地方变量的代码表中。如果同一个地方变量是通过使用多种多样的EDOCX1标语2(英文)Calls而被赋予价值的话,那么它只是在最初的数据上超越了旧的价值——它并不意味着分配更多的空间。
- EDOCX1 3:Uses an already-allocated storage location and just wipes the data.这是用于我们所有的参数建设者的呼叫,包括那些指派给地方变量的。对于方法呼吁,一个中间的地方变量是有效的介绍,其价值是由EDOCX1。
(P)I hope this shows how complicated the topic is,while shining a bit of light on it at the same time.在一些概念性的感知中,每个人都呼吁在斯塔克-但作为我们所看到的空间中使用EDOCX1,这是什么实际发生的甚至在一个层次上。I'd like to highlight one particular case.采用这一方法:好的,好的。字母名称(P)"Logically"has 4 stack allocations-one for the variable,and one for each of the three EDOCX1 substantial 2 americano calls-but in fact(for that specific code)the stack is only allocated once,and they the same storage location is reused.好的,好的。(P)Edit:Just to be clear,this is only true in some cases…尤其是,如果"EDOC X1"的英文字母值为7,那么,英文字母7的英文值就不会被人看到;建筑商Throws an Exception,which is why the C§35;compiler is able to reuse the same stack slot。See Eric Lippert's blog post on value type construction for more details and a case where it doesn't apply.好的,好的。(P)I've learned a lot in writing this answer-please ask for clarification if any of it is unclear!好的,好的。好吧。
根据情况,可以在堆栈或堆上分配包含结构字段的内存。如果结构类型变量是某个匿名委托或迭代器类未捕获的局部变量或参数,那么它将在堆栈上分配。如果变量是某个类的一部分,那么它将被分配到堆上的类中。
如果结构是在堆上分配的,那么实际上不需要调用new运算符来分配内存。唯一的目的是根据构造函数中的内容设置字段值。如果不调用构造函数,则所有字段都将获得其默认值(0或空)。
类似地,对于在堆栈上分配的结构,除了c要求所有局部变量在使用之前都设置为某个值,因此必须调用自定义构造函数或默认构造函数(不带参数的构造函数始终可用于结构)。
简而言之,new是结构的误称,调用new只是调用构造函数。结构的唯一存储位置是它被定义的位置。
如果它是一个成员变量,它将直接存储在定义它的任何地方;如果它是一个局部变量或参数,它将存储在堆栈上。
将其与类进行对比,这些类具有引用,而引用指向堆中的某个位置,而引用将存储整个结构。(成员在堆栈中,本地/参数在堆栈中)
它可能有助于对C++进行一点了解,在类/结构之间没有真正的区别。(语言中有类似的名称,但它们只指事物的默认可访问性)当调用new时,您得到指向堆位置的指针,而如果您有非指针引用,则它直接存储在堆栈或其他对象中,而ala结构则存储在c。
与所有值类型一样,结构总是指向声明它们的位置。
有关何时使用结构的详细信息,请参阅此处的此问题。这里的这个问题是关于结构的更多信息。
编辑:我错误地回答他们总是一窝蜂。这是不正确的。
我可能在这里遗漏了一些东西,但为什么我们关心分配呢?
值类型是通过value;)传递的,因此不能在定义它们的不同范围内进行变异。要改变值,必须添加[Ref]关键字。
引用类型是通过引用传递的,并且可以改变。
当然,最流行的是不可变的引用类型字符串。
阵列布局/初始化:值类型->零内存[名称,zip][名称,zip]引用类型->零内存->空[Ref][Ref]
一般来说,
为了更多…
大多数被认为是值类型的结构都是在堆栈上分配的,而对象是在堆上分配的,而对象引用(指针)是在堆栈上分配的。
结构被分配给堆栈。以下是一个有用的解释:
结构体
Additionally, classes when instantiated within .NET allocate memory on
the heap or .NET's reserved memory space. Whereas structs yield more
efficiency when instantiated due to allocation on the stack.
Furthermore, it should be noted that passing parameters within structs
are done so by value.