结构体内存对齐规则:
1.第一个成员在与结构体变量偏移量为0 的地址处。
2.其他成员变量要对齐到某个数字(对其数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的 较小值。
vs 中默认的值为 8
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含嵌套结构体的对齐数)的整数倍。
实际应用
#include<stdio.h> struct s1 { char c1; int a; char c2; }; struct s2 { char c1; char c2; int a; }; int main() { struct s1 s1 = {0}; printf("%d\n",sizeof(s1)); struct s2 s2 = {0}; printf("%d\n",sizeof(s2)); return 0; }
这段代码输出的结果为:
12
8
下面我们来逐一分析:
s1
c1为char型,占一个字节,第一个成员即 c1 在与结构体变量偏移量为0 的地址处。
a为int型,占四个字节,对齐数是4,对齐到4的整数倍的地址处,即偏移量为4开始的地址空间。
c2为char型,占一个字节,对齐数是1,对齐到1的整数倍的地址处,即偏移量为8开始的地址空间。
结构体总大小为最大对齐数的整数倍,所以为对齐数4的整数倍,现在已经用了9个字节的空间,那么总大小就是12个字节空间。
所以输出结果是12。
s2
c1为char型,占一个字节,第一个成员即 c1 在与结构体变量偏移量为0 的地址处。
c2为char型,占一个字节,对齐数是1,对齐到1的整数倍的地址处,即偏移量为1开始的地址空间。
a为int型,占四个字节,对齐数是4,对齐到4的整数倍的地址处,即偏移量为4开始的地址空间。
结构体总大小为最大对齐数的整数倍,所以为对齐数4的整数倍,现在已经用了8个字节的空间,那么总大小就是8个字节空间。
所以输出结果是8。
结构体的嵌套使用情况
#include<stdio.h> struct s3 { double d; char c; int i; }; struct s4 { char a; struct s3 s3; double b; }; int main() { struct s4 s4; printf("%d\n",sizeof(s4)); return 0; }
结果为:32
由以上方法容易得出struct S3占16个字节,最大对齐数为:8。
a为char型,占一个字节,第一个成员即 c1 在与结构体变量偏移量为0 的地址处。
第二个成员是结构体嵌套使用,结构体S3变量s3,刚才已经得出占16个字节,最大对齐数是8,所以对齐到偏移量为8的地址空间。
b为double型,占八个字节,对齐数是8,对齐到8的整数倍的地址处,即偏移量为24开始的地址空间。
结构体总大小为最大对齐数的整数倍,所以为对齐数8的整数倍,现在已经用了32个字节的空间,那么总大小就是32个字节空间。
所以输出结果是32。
s3
s4
为什么存在内存对齐:
1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否者抛出硬件异常。
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说:
结构体的内存对齐是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起。
struct s1 { char c1; int a; char c2; }; struct s2 { char c1; char c2; int a; };
s1 和 s2 类型的成员一模一样,但是s1和s2所占的空间的大小有了一些区别。
设置默认对齐数:
#pragma pack(8)//设置默认对齐数为8 struct s1 { char c1; int i; char c2; }; #pragma pack()//取消设置的默认对齐数,还原为默认