The MSDN clearly states
For all other types, including structs, the sizeof operator can only be used in unsafe code blocks.
The C#
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.
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.