结构体对齐三大法则
1、自然对齐规则:结构体的成员将按照自然对齐原则来排列。自然对齐要求每个成员的地址是其数据类型大小的整数倍。例如,一个 int类型的成员通常会被对齐到4字节边界(整除4),而一个 double 类型的成员通常会被对齐到8字节边界。
2、填充字节:如果一个成员的大小不是对齐边界的整数倍编译器通常会在其后添加一些填充字节,以确保下一个成员能够正确对齐。填充字节的大小取决于编译器和目标平台,通常是1字节或更多。
3、结构体的总大小:结构体的总大小通常等于其最大成员的大小的整数倍。这是为了确保结构体数组的每个元素都按照相同的规则对齐。
结构体对齐是编译器在处理结构体时,为了提高内存访问效率和保证数据安全,对结构体成员变量在内存中的布局进行调整的规则,以下是根据三大法则详细讲解 C 语言中的结构体对齐法则:
自然对齐规则
- 定义 :每个结构体成员按照其自身类型的标准对齐方式(通常与其本身的大小相同,即多少字节类型就按照几字节对齐)来安排在内存中的位置,即成员变量的起始地址必须是其类型大小的整数倍。
- 举例 :例如,对于一个
int
类型(假设其占 4 字节),它会被对齐到 4 字节边界,也就是说,它的起始地址必须能被 4 整除;一个char
类型(占 1 字节)则可以放在任何地址,因为它对齐到 1 字节边界;一个double
类型(假设占 8 字节)要对齐到 8 字节边界。
填充字节
- 产生原因 :由于结构体中各成员的类型不同,它们的对齐要求也不同。当一个成员的大小不是其对齐边界的整数倍时,编译器会在该成员之后添加一些填充字节,以确保下一个成员能够按照其自身的对齐要求正确对齐。
- 填充字节数计算 :假设前一个成员的大小为 n 字节,其对齐要求为 k 字节(k 通常是 n 的值),下一个成员的对齐要求为 m 字节,那么当前成员结束到下一个成员开始之间的填充字节数等于(m - n % m)% m。
- 举例 :假设有结构体
struct test1
,定义如下:
struct test1 {char a; // 1字节,对齐到1字节边界int b; // 4字节,对齐到4字节边界
};
char a
占 1 字节,起始地址假设为 0,下一个成员 int b
要对齐到 4 字节边界,所以从地址 1 到地址 4 之间有 3 个字节的间隙,编译器会在这 3 个字节处填充,以满足 int b
的对齐要求。因此,结构体 test1
实际占用的内存为 1(a) + 3(填充) + 4(b) = 8
字节。
结构体的总大小
- 计算规则 :结构体的总大小是其最大成员的大小的整数倍。这是为了确保当结构体形成数组时,每个结构体元素都能按照相同的规则对齐。
- 举例 :假设有一个结构体
struct test2
,定义如下:
struct test2 {int a; // 4字节,对齐到4字节边界double b; // 8字节,对齐到8字节边界char c; // 1字节,对齐到1字节边界
};
其中最大的成员是 double
类型的 b
,占 8 字节。按照结构体总大小为最大对齐边界大小的整数倍规则,先计算各成员实际占用的内存:
a
占 4 字节,b
占 8 字节,c
占 1 字节。- 在
a
和b
之间,由于b
要对齐到 8 字节边界,a
结束后的地址是 4 字节,需要填充 4 字节才能满足b
的对齐要求,所以a
和b
之间填充 4 字节。 b
结束后是c
,c
只需要对齐到 1 字节边界,所以无需填充,直接紧挨着b
放置。- 此时,结构体的总大小为
4(a) + 4(填充) + 8(b) +1(c) =17
字节,但因为最大对齐边界是 8 字节,所以要将 17 向上取整为 8 的整数倍,即 24 字节,所以在结构体最后还需要填充 7 字节,使得结构体总大小为 24 字节。
注意事项
- 默认对齐方式 :在 Visual Studio 中,结构体默认按照最大对齐边界来对齐。可以使用
#pragma pack
来改变对齐方式,指定字节对齐方式或恢复默认对齐方式。 - 结构体对齐的目的 :主要是为了提高内存访问速度。现代计算机中,内存访问速度快慢与内存地址有关,当数据对齐时,CPU 可以在一次操作中访问到所需的数据,从而提高程序运行效率。
- 不同编译器和平台的差异 :不同的编译器和目标平台可能会有不同的对齐规则和默认对齐方式,因此在编写跨平台代码时需要特别注意结构体的对齐问题。
技巧
- 根据这个规则,因为大多数的基础数据类型都是偶数字节,少部分是奇数字节
(char)
,故而这些基础数据类型都会对齐偶数位的内存地址,所以可以推出:除非结构体内的成员变量的数据类型全为奇数个char
(若成员变量有数组,则把数组摊开来看),否则结构体的实例大小一定是偶数倍字节。且填充数量一定是使得结构体的实例大小为最大成员变量大小能够囊括的住的最小倍数。