C# 为什么结构的sizeof是不安全的

C# 为什么结构的sizeof是不安全的,c#,struct,sizeof,C#,Struct,Sizeof,报告明确指出 对于所有其他类型,包括结构,sizeof运算符只能 不能在不安全的代码块中使用 更准确的说法是: 未指定将成员打包到结构中的顺序 出于对齐目的,开头可能有未命名的填充 在一个结构中,在一个结构中,在结构的末尾 用作填充的位的内容是不确定的 当应用于具有结构类型的操作数时,结果是该类型变量中的总字节数,包括任何填充 但是,CLR将如何处理以下结构: [StructLayout(LayoutKind.Explicit, Size = 1, Pack = 1)] public struc

报告明确指出

对于所有其他类型,包括结构,sizeof运算符只能 不能在不安全的代码块中使用

更准确的说法是:

  • 未指定将成员打包到结构中的顺序
  • 出于对齐目的,开头可能有未命名的填充 在一个结构中,在一个结构中,在结构的末尾
  • 用作填充的位的内容是不确定的
  • 当应用于具有结构类型的操作数时,结果是该类型变量中的总字节数,包括任何填充
  • 但是,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(myemptystuct)
    计算这些结构的大小时,编译器抛出以下错误:

    “*”没有预定义的大小,因此sizeof只能 在不安全的环境中使用


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

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

    在MyStruct中,我们明确地执行布局

    你没有。[StructLayout]属性只有在封送结构值时才真正有效。StructureToPtr(),也由pinvoke封送员使用。只有这样,才能保证封送的值具有请求的布局。CLR保留其认为合适的布局结构的权利。它将对齐结构成员,以便使用结构的代码尽可能快,必要时插入空字节。如果这样的填充字节留有足够的空间,它甚至会交换成员以获得更小的布局。除了使用调试器查看访问结构成员的机器代码之外,这是完全不可发现的。某些[StructLayout]属性确实会影响布局LayoutKind。Explicit实际上支持声明联合。映射算法的确切细节没有文档记录,可能会发生更改,并且在很大程度上取决于目标机器的体系结构

    结果是该类型变量的总字节数,包括任何填充

    事实并非如此,实际结构可以小于声明的结构。可以通过将成员交换到填充中来实现

    这个结构应该在内存中有一个1字节的大小

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

    MyEmptyStruct为空,我们可以假设内存中的大小为0字节

    即使结构为空,变量始终至少有1个字节。这避免了诸如具有占用零字节的非空数组之类的歧义。还有其他语言的规则,比如C++ + < /P> 为什么在这种情况下使用sizeof被认为是不安全的

    需要明确的是,在原语值类型上使用sizeof并不需要不安全,因为.NET2。但对于结构,sizeof()可能直接用于寻址内存,例如将其添加到IntPtr中。使用sizeof()可能是错误的选择,应该改为Marshal.sizeof()。我猜在结构上使用sizeof()的实用性很低,因为一个结构应该总是很小的,并且以错误的方式攻击intptr的几率很高,以至于他们让它变得不安全

    我想知道为什么在这种情况下使用sizeof被认为是不安全的

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



    (*)说句公道话,safe
    sizeof
    可以接受包含托管类型的结构,这里有一些奇怪的角落用法。例如,假设您有一个通用集合类,该类将分配一组数组,并希望确保这些数组不会移动到大型对象堆中;如果您可以采用包含托管对象的结构的大小,那么您可以非常轻松地编写此代码,并且不需要任何指针算法。但事实仍然是,
    sizeof
    是专门为指针算法设计的,而不是为了让您可以对数组的垃圾收集启发式进行结束运行。

    Skeet在这里的回答是:很好。我还没有看到它不安全的原因。我猜编译器需要它来强化这样一个概念,即
    sizeof(struct)
    将根据x86/x64设置等而变化,因此这样做有点不安全。但是仅仅询问结构的大小并不是不安全的,正如获取和使用指向内存块的指针是不安全的一样。这不是上面链接的问题的重复-另一个问题问你为什么不能得到a的大小