【编程】C#中的结构体字节对齐
基本原则
- 结构体变量的首地址能够被其对齐字节数大小所整除
- 成员相对结构体首地址的偏移是【当前成员对齐数】的整数倍,不满足对前一成员填充字节
- 结构体总大小为【最大成员对齐数】的整数倍
成员对齐数,以下三者取最小值:
- 类型的非托管字节数
- Pack值
- 当前环境默认对齐字节数,例如Window 64位环境下默认是8字节
从实例看注意事项
例一
[StructLayout(LayoutKind.Sequential, Pack = 2)]
class Foo
{
// 整型 成员大小 计算下一成员首地址 下一成员对齐数 下一成员真实首地址
public sbyte i1; // 1 +0 = 1 [1] +0 = 1
public byte i2; // 1 +1 = 2 2 +0 = 2
public short i3; // 2 +2 = 4 2 +0 = 4
public ushort i4; // 2 +4 = 6 2 +0 = 6
public int i5; // 4 +6 = 10 2 +0 = 10
public uint i6; // 4 +10 = 14 2 +0 = 14
public long i7; // 8 +14 = 22 2 +0 = 22
public ulong i8; // 8 +22 = 30 2 +0 = 30
public nint i9; // [8] +30 = 38 2 +0 = 38
public nuint i10; // [8] +38 = 46 2 +0 = 46
// 浮点型
public float f1; // 4 +46 = 50 2 +0 = 50
public double f2; // 8 +50 = 58 2 +0 = 58
public decimal f3; // 16 +58 = 74 2 +0 = 74
// 其他
public bool b; // 4[1] +74 = 78 2 +0 = 78
public char c; // 1[2] +78 = 79 [2] +1 = 80
} //最大成员对齐数 结构体的非托管字节数
首先看 i2 的对齐数,这个结构体定义 Pack 值为 2,byte 类型占 1 字节,我的环境是 Windows 64 默认对齐字节数是 8,三者取最小,i2 的对齐数应该 1。因此第一行中 i1 是不需要对齐的。
之后看 i9 和 i10 的对齐数,这两个类型的非托管字节数和环境有关,我的环境中是 8 字节,然而结构体开头就定义了 Pack 值为 2,取最小值仍旧会取到 2。i8 占位后,下一成员首地址为 30,能够被 2 整除。因此这一行也不需要对齐。
最后看一下 bool 和 char,这是两个很特殊的存在。如果使用 sizeof
求 bool 的字节数为 1,求 char 的字节数为 2,但是使用 Marshal.SizeOf
求 bool 的非托管字节数为 4,求 char 的非托管字节数为 1。计算字节对齐要聚焦非托管字节数,即 bool 取 4,char 取 1。
例二
struct Bar
{
// 整型
public sbyte i1; // 1 +0 = 1 1 +0 = 1
public byte i2; // 1 +1