Why sizeof of a struct is unsafe

The MSDN clearly states

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

The C# Language Specification is even more precise :

  • The order in which members are packed into a struct is unspecified.
  • For alignment purposes, there may be unnamed padding at the beginning of a struct, within a struct, and at the end of the struct.
  • The contents of the bits used as padding are indeterminate.
  • When applied to an operand that has struct type, the result is the total number of bytes in a variable of that type, including any padding.
  • However how would the CLR handle the following structures :

    [StructLayout(LayoutKind.Explicit, Size = 1, Pack = 1)]
    public struct MyStruct
    {
        [FieldOffset(0)] public byte aByte;
    }
    
    public struct MyEmptyStruct { }
    

    In MyStruct we enforce the layout explicitly, the size and how to pack it via the StructLayout attribute. This structure is supposed to have a size of 1 byte in memory.

    On the other hand MyEmptyStruct is empty, we can assume that the size in memory will be 0 bytes - even if a such structure is most likely not going to be used it still is an interesting case.

    When trying to compute the size of theses structures using sizeof(MyStruct) and sizeof(MyEmptyStruct) the compiler throws the following error :

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

    I would like to know why using sizeof in this context is considered unsafe . The question is not intended to ask for workarounds nor the correct way to compute the size of a struct but rather to focus on the causes.


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

    Matthew Watson's comment hits the nail on the head. What are you going to do with that information in safe code? It's not useful for anything(*). It doesn't tell you how many unmanaged bytes you need to allocate to marshal; that's Marshal.SizeOf . It's only useful for pointer arithmetic, so why should it be in the safe subset?


    (*) OK to be fair there are a few odd corner case usages for a safe sizeof that can take structs that contain managed types. Suppose for example you have a generic collection class that is going to allocate a bunch of arrays and would like to ensure that those arrays are not moved into the large object heap; if you could take the size of a struct that contained managed objects then you could write this code very easily, and it would not need any pointer arithmetic. But the fact remains that sizeof was designed specifically for pointer arithmetic, and not so that you could do an end-run around the garbage collection heuristics for arrays.


    Lots of wrong assumptions in the question, I'll just address them one by one:

    in MyStruct we enforce the layout explicitly

    You didn't. The [StructLayout] attribute is only truly effective when the structure value is marshaled. Marshal.StructureToPtr(), also used by the pinvoke marshaller. Only then do you get the guarantee that the marshaled value has the requested layout. The CLR reserves the right to layout the structure as it sees fit. It will align structure members so the code that uses the struct is as fast as possible, inserting empty bytes if necessary. And if such padding bytes leave enough room then it will even swap members to get a smaller layout. This is entirely undiscoverable, other than by using a debugger to look at the machine code that accesses the structure members. Some [StructLayout] properties do affect the layout, LayoutKind.Explicit does in fact support declaring unions. The exact details of the mapping algorithm is undocumented, subject to change and strongly depends on the target machine architecture.

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

    It is not, the actual structure can be smaller than the declared struct. Possible by swapping a member into the padding.

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

    That's very rarely the case. Local variables are also aligned in memory, by 4 bytes on a 32-bit processor and 8 bytes in a 64-bit processor. Unless the struct is stored in an array, it will actually take 4 or 8 bytes on the stack or inside an object on the heap. This alignment is important for the same reason that member alignment is important.

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

    A variable will always have at least 1 byte, even if the struct is empty. This avoids ambiguities like having a non-empty array that takes zero bytes. Also the rule in other languages, like C++.

    why using sizeof in this context is considered unsafe

    To be clear, using sizeof on primitive value types doesn't require unsafe since .NET 2. But for structs there is a definite possibility that sizeof() might be used to address memory directly, adding it to an IntPtr for example. With the considerable risk that using sizeof() was the wrong choice and should have been Marshal.SizeOf() instead. I would guess that the practicality of using sizeof() on structs is so low, given that a struct should always be small, and the odds for hacking IntPtrs the wrong way is so high that they left it unsafe.

    链接地址: http://www.djcxy.com/p/72140.html

    上一篇: C:char指针和数组之间的区别

    下一篇: 为什么sizeof结构是不安全的