关于c#:为什么struct的sizeof是不安全的

Why sizeof of a struct is unsafe

msdn清楚地说明

For all other types, including structs, the sizeof operator can only
be used in unsafe code blocks.

C语言规范更加精确:

  • 未指定成员打包到结构中的顺序。
  • 出于对齐目的,可能在开始处有未命名的填充结构的,在结构内,在结构的末尾。
  • 用作填充的位的内容不确定。
  • 当应用于具有结构类型的操作数时,结果是该类型变量中的字节总数,包括任何填充。
  • 但是,clr将如何处理以下结构:

    1
    2
    3
    4
    5
    6
    7
    [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)计算这些结构的大小时,编译器抛出以下错误:

    '*' does not have a predefined size, therefore sizeof can only
    be used in an unsafe context

    我想知道为什么在这种情况下使用sizeof被认为是unsafe。这个问题不是为了寻求解决方法,也不是计算结构大小的正确方法,而是关注原因。


    I would like to know why using sizeof in this context is considered unsafe.

    马修·沃森的评论直言不讳。你打算用安全代码处理这些信息吗?它对任何东西都没有用处。它不会告诉您需要分配多少非托管字节来封送;这就是Marshal.SizeOf。它只对指针算术有用,那么为什么它应该在安全子集中呢?

    (*)可以公平地说,对于安全的sizeof有一些奇怪的用法,它们可以采用包含托管类型的结构。例如,假设您有一个通用集合类,它将分配一组数组,并希望确保这些数组不会移动到大型对象堆中;如果您可以采用包含托管对象的结构的大小,那么您可以非常容易地编写此代码,并且它不需要任何指针算术。但事实仍然是,sizeof是专门为指针算术而设计的,而不是为了让您可以围绕数组的垃圾收集启发进行最终运行。


    在这个问题中有很多错误的假设,我将逐一解决:

    in MyStruct we enforce the layout explicitly

    您没有。只有在整理结构值时,[structlayout]属性才真正有效。marshal.structureToptr(),也由pinvoke marshaller使用。只有这样,才能保证封送值具有请求的布局。CLR保留在其认为合适时对结构进行布局的权利。它将对齐结构成员,以使使用该结构的代码尽可能快,并在必要时插入空字节。如果这样的填充字节留下足够的空间,那么它甚至会交换成员以获得更小的布局。这是完全不可恢复的,除了使用调试器查看访问结构成员的机器代码。某些[structlayout]属性确实会影响layout、layoutKind。explicit实际上支持声明联合。映射算法的确切细节是未记录的,可能会发生更改,并且很大程度上取决于目标机器结构。

    the result is the total number of bytes in a variable of that type, including any padding.

    不是,实际结构可以小于声明的结构。可以通过将成员替换到填充中来实现。

    This structure is supposed to have a size of 1 byte in memory.

    这种情况很少发生。本地变量也在内存中对齐,在32位处理器上为4个字节,在64位处理器上为8个字节。除非结构存储在数组中,否则它在堆栈上或在堆上的对象内实际将占用4或8个字节。此对齐非常重要,原因与构件对齐非常重要相同。

    MyEmptyStruct is empty, we can assume that the size in memory will be 0 bytes

    即使结构为空,变量也将始终至少有1个字节。这样就避免了不明确的地方,比如有一个非空数组,它占用零字节。还有其他语言的规则,比如C++。

    why using sizeof in this context is considered unsafe

    要清楚,在基元值类型上使用sizeof并不要求不安全,因为.NET 2。但是对于结构来说,sizeof()可能被用于直接寻址内存,例如将其添加到intptr中。有相当大的风险,使用sizeof()是错误的选择,应该改为marshal.sizeof()。我猜在结构上使用sizeof()的实用性是如此之低,因为结构应该总是很小的,而以错误的方式入侵intptr的几率是如此之高,以至于它们使其不安全。