C#中含ushort的结构体设置[StructLayout(LayoutKind.Sequential,Pack=4)]为何不生效?
C#结构体Pack=4时大小不符合预期的原因解析
示例代码
[StructLayout(LayoutKind.Sequential, Pack = 4)] public struct OOP_DATE_T //tag_Date { public ushort year; public byte month; public byte mday; public byte wday; }; public class Example { public unsafe static void Main() { OOP_DATE_T ex = new OOP_DATE_T() { year = 2022, month = 12, mday = 1, wday = 1 }; byte* addr = (byte*)&ex; Console.WriteLine("Size: {0}", sizeof(OOP_DATE_T)); //Console.WriteLine(Marshal.SizeOf<OOP_DATE_T>()); Console.WriteLine("year Offset: {0}", (byte*)&ex.year - addr); Console.WriteLine("month Offset: {0}", &ex.month - addr); Console.WriteLine("mday Offset: {0}", (byte*)&ex.mday - addr); Console.WriteLine("wday Offset: {0}", (byte*)&ex.wday - addr); } }
问题现象
运行代码后输出:
Size: 6 year Offset: 0 month Offset: 2 mday Offset: 3 wday Offset: 4
按照4字节对齐规则,预期结构体大小应为8,但实际是6。设置Pack=1和Pack=2时sizeof结果符合预期,而在结构体中添加int变量后,Pack=4也能让sizeof结果按4字节对齐。
原因解析
核心是**sizeof和Marshal.SizeOf的计算规则完全不同**:
[StructLayout]的Pack属性是为非托管互操作设计的,它控制的是结构体在非托管内存中的布局和大小,对应的是Marshal.SizeOf的计算结果。如果你把代码中注释的Marshal.SizeOf打开,会得到结果8,这才是符合4字节对齐的非托管大小。- 你用的
sizeof(OOP_DATE_T)计算的是CLR托管内存中结构体的大小,CLR会按自身逻辑优化布局:- 字段对齐取「字段自身大小」和
Pack值的较小值。你的结构体最大字段是ushort(2字节),Pack=4,所以字段按2字节对齐:year占0-1字节,month在2,mday在3,wday在4,总长度5字节。 - 结构体整体大小会向上对齐到「最大字段大小」和
Pack值的较小值(也就是2字节),所以5向上取整到6,这就是输出Size=6的原因。
- 字段对齐取「字段自身大小」和
为什么其他场景看起来正常?
- 当
Pack=1或Pack=2时,「最大字段大小」和Pack值的关系刚好匹配,sizeof的结果和你预期的对齐规则一致。 - 添加
int变量后,最大字段大小变成4,和Pack=4相等,结构体整体对齐到4字节,sizeof结果自然变成4的倍数,看起来Pack=4生效了。
内容的提问来源于stack exchange,提问作者garen.X
相关产品推荐
相关产品推荐

