为什么sizeof结构是不安全的
MSDN明确指出
对于所有其他类型,包括结构体,sizeof运算符只能用于不安全的代码块。
C#语言规范更加精确:
但是,CLR如何处理以下结构:
[StructLayout(LayoutKind.Explicit, Size = 1, Pack = 1)]
public struct MyStruct
{
[FieldOffset(0)] public byte aByte;
}
public struct MyEmptyStruct { }
在MyStruct
我们显式地强制布局,大小以及如何通过StructLayout
属性打包它。 该结构应该在内存中具有1个字节的大小。
另一方面, MyEmptyStruct
是空的,我们可以假设内存中的大小是0字节 - 即使这样的结构很可能不会被使用,它仍然是一个有趣的案例。
当试图使用sizeof(MyStruct)
和sizeof(MyEmptyStruct)
来计算这些结构的大小时,编译器会引发以下错误:
'*'没有预定义的大小,因此sizeof只能用于不安全的上下文中
我想知道为什么在这种情况下使用sizeof
被认为是unsafe
。 这个问题不是要求解决方法,也不是要求计算结构大小的正确方法,而是要关注原因。
我想知道为什么在这种情况下使用sizeof被认为是不安全的。
马修沃森的评论击中了头部。 你将如何处理这些安全代码中的信息? 这对任何事情都没有用(*)。 它不会告诉你需要分配给编组的多少个非托管字节; 那是Marshal.SizeOf
。 它只对指针算术有用,为什么它应该在安全子集中?
(*)可以公平地说,有一些奇怪的角落案例用于安全sizeof
,可以采取包含托管类型的结构。 假设你有一个通用的集合类,它将分配一堆数组,并希望确保这些数组不会移动到大对象堆中; 如果你可以采用包含托管对象的结构的大小,那么你可以很容易地编写这些代码,并且它不需要任何指针算术。 但事实仍然是sizeof
是专门为指针算术设计的,而不是你可以围绕数组的垃圾收集启发式做一个末端运行。
在这个问题上有很多错误的假设,我会逐一解决它们:
在MyStruct中,我们明确地强制布局
你没有。 当结构值被编组时,[StructLayout]属性才真正有效。 Marshal.StructureToPtr(),也被pinvoke编组使用。 只有这样才能保证编组值符合要求的布局。 CLR保留根据其认为适当的布局结构的权利。 它将对齐结构成员,以便使用该结构的代码尽可能快,必要时插入空字节。 如果这种填充字节留下足够的空间,那么它甚至会交换成员以获得更小的布局。 除了使用调试器来查看访问结构成员的机器代码以外,这完全是不可发现的。 一些[StructLayout]属性确实会影响布局,LayoutKind.Explicit实际上支持声明联合。 映射算法的确切细节未经证实,可能会有所变化,并且主要取决于目标机器架构。
结果是该类型变量的总字节数,包括任何填充。
不是,实际的结构可能比声明的结构小。 可以通过交换成员到填充。
该结构应该在内存中具有1个字节的大小。
情况非常少见。 局部变量也在内存中对齐,在32位处理器上为4个字节,在64位处理器中为8个字节。 除非结构体存储在一个数组中,否则它实际上需要4或8个字节的堆栈或堆内的对象。 这种对齐非常重要,因为成员对齐很重要。
MyEmptyStruct是空的,我们可以假设内存中的大小是0字节
即使结构为空,变量也将始终有至少1个字节。 这避免了含有零字节的非空数组之类的含糊之处。 也是其他语言的规则,如C ++。
为什么在这种情况下使用sizeof被认为是不安全的
要明确的是,在基本值类型上使用sizeof并不需要从.NET 2开始的不安全。但是对于结构,sizeof()可能会直接用于直接寻址内存,并将其添加到IntPtr中。 有相当大的风险,使用sizeof()是错误的选择,应该是Marshal.SizeOf()代替。 我猜测在结构体上使用sizeof()的实用性非常低,因为一个结构体应该总是很小,并且以错误的方式攻击IntPtrs的几率太高,以致于它们不安全。
链接地址: http://www.djcxy.com/p/72139.html