unsafe static void Main()
{
int a = 2, b = 4;
Console.WriteLine("Adress of a : {0}", (int)&a);
Console.WriteLine("Adress of b : {0}", (int)&b);
Console.WriteLine("Size of integer: {0}", (int)(&a) - (int)(&b));
}
输出:
Adress of a : 1372876
Adress of b : 1372872
Size of integer: 4
CLR是否对整数和其他值类型(float,long,double,...)进行特殊处理?
答案 0 :(得分:15)
不,它们是值类型的事实并不意味着它们存储在堆栈中。这意味着它们已存储wherever the variable lives。
但是,嘿,让我们推动本地变量业务,此时(没有捕获等)他们做在堆栈上生存。它们需要4个字节。为什么他们需要更多?堆栈上不需要vtable,因为元数据已经指定了类型:对于将调用哪些虚拟方法等没有歧义。
编辑:正如Shawn的评论所指出的那样(但我想让它更明显),System.Int32是一个结构,而不是一个类。 (实际上CLR会创建一个阴影引用类型来覆盖整数的盒装值,但这是另一回事。)答案 1 :(得分:5)
因此,它们应该在堆栈上占用超过四个字节。
这不遵循。编译器和运行时知道确切的类型。值类型不能进一步子类型化,因此不需要“vtable”或其他特定于对象的动态调度机制。
当值类型被装箱以将它们放在堆上时,需要正常的.NET Object头。
答案 2 :(得分:4)
如果值类型是方法中的局部变量,则在堆栈上分配值类型。如果值类型是类的成员,它将被分配为堆上对象的内存区域的一部分。
值类型变量不需要任何额外的数据来跟踪类型,就像引用类型一样。编译器总是知道值类型变量的位置以及它们的类型,因此除了实际数据之外不需要额外的数据。 Int32变量总是四个字节。
在堆上分配引用类型,它具有指向它的引用(或更多)。引用本身实际上是一个值类型,因此它只是一个指针,编译器会跟踪它的位置以及它的类型。引用的类型不必与它指向的对象的类型相同,因此对象需要额外的信息来跟踪类型。例如,指向StringBuilder类的实例的对象引用:
object o = new StringBuilder();
这里编译器跟踪引用的类型是对象,因此它只是一个指针(32位应用程序中的4个字节)。 StringBuilder对象存储在堆上,它有两个额外的指针,用于跟踪实际类型。
值类型也可以加框,即存储为堆上的对象。将值类型转换为Object:
时会发生这种情况object p = 42;
这将在堆上分配一个对象并将整数的值复制到其中。此对象需要额外的类型信息来跟踪类型,因此它将在堆上使用12个字节而不是4个(在32位应用程序中)。
答案 3 :(得分:0)
类型定义与为该类型的实例存储的值之间存在差异,例如......
// type definition
public class Bla {}
// instance of type bla
public Bla myBla = new Bla();
本质上int的大小就像它看起来的那样(4个字节),但正如你所知,这是它需要声明的内存空间的大小。
类型定义存储在别处,像CompareTo这样的方法只会以这种方式声明,而不是为你声明的那个类型的每个实例声明一次,因为它们作为框架库本身的一部分加载,用于你的应用程序,这些定义有效地占用了0个空间。