void main()
{
int x = 5; // stack-allocated
Console.WriteLine(x);
}
我知道x
是堆栈分配的。但实际存储在堆栈中的x
是什么?它是否包含“实际值”,或者包含值的内存中的某个地址?
答案 0 :(得分:8)
答案是响亮的“它取决于”。
有一点是肯定的:局部变量x
的存储(如果存在的话)包含实际值而不是引用,因为int
是值类型。
在简洁的脑translation翻译中,x
的实际值(不是指针或对它的引用)存储在当前调用的激活记录中的局部变量中。生成的IL将是这样的:
.maxstack 1
.locals init (int32 V_0)
ldc.i4.5 // push 5 on the evaluation stack
stloc.0 // pop it into x
ldloc.0 // now push a copy of x to pass to ...
call void [mscorlib]System.Console::WriteLine(int32)
ret // return
(请注意,.maxstack
指令正在讨论IL评估堆栈,不一定与本机堆栈相对应。评估堆栈(如果浅)通常由JIT编译的代码保存在寄存器中。)
在CLR的大多数当前实现中,激活记录及其局部变量将存储在本机堆栈中。所以,天真地,我认为x
可以被认为是将其值存储在堆栈中。
在上述代码的优化编译(或优化JIT转换)中,IL中根本不存在局部变量存储。毕竟,为什么要分配局部变量存储来存储它,然后浪费两个使用该局部变量的指令,当你再也不需要它时?它可以作为WriteLine
的常量参数内联:
.maxstack 1
// Just pass 5 to WriteLine. No local variables here.
ldc.i4.5
call void [mscorlib]System.Console::WriteLine(int32)
// Bye.
ret
还有更多可能的并发症。如果函数在C#5 CTP中声明为async
,或者函数中其他位置的lambda表达式捕获x
,或者函数是使用yield return
构建的枚举器声明,x
的存储可能最终强制进入堆:它将成为编译器生成的类中的一个字段,这样如果闭包或延续转义本地,变量的存储可以在堆栈帧的拆除中存活下来范围。
答案 1 :(得分:0)
它存储x的实际值。没有地址或其他东西。
答案 2 :(得分:0)
我想这行已转换为ldind.i
命令。规格说:
4D
ldind.i
将地址addr中的native int值作为native int加载到堆栈中。
答案 3 :(得分:0)
可能由于int
的字节大小将小于或等于处理器的寄存器大小,因此很难预测CLR会选择这样做的情况。
然而,尽管这可能具有学术意义,但在使用C#编写程序时不需要考虑。它的实现细节,它可以改变,代码仍然可以工作。 CLR实施者使我们摆脱了这一责任。