1、结构体内存对齐规则
先来让我们来了解一下结构体内存对齐的规则
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(如果编译器没有默认对齐数,那对齐数>就是成员自身的对齐数)- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,也就是说结构体大小不能小于其最大成员大小;
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
2、结构体内存对齐示例
#include <stdio.h>
struct s1
{
char c;
int i;
short s;
};
struct s2
{
double d;
float f;
struct s1 A;
};
struct s3
{
struct s1 B;
struct s2 C;
char str[10];
};
int main()
{
printf("Size of s1: %d\n", sizeof(struct s1));
printf("Size of s2: %d\n", sizeof(struct s2));
printf("Size of s3: %d\n", sizeof(struct s3));
return 0;
}
来看一下它的输出结果
先来分析一下s1
c是char型,占1个字节,对应到结构体变量偏移量为0 的地址处。
i是int型,占4个字节,对齐数就是4,对齐到4的整数倍位置处,即偏移量为4开始的地址处。
s是short型,占2个字节,对齐到1 的整数倍,那就是下一个地址空间,对齐到偏移量为8的地址空间。
结构体总大小为最大对齐数的整数倍,最大对齐数为4,故总大小是4的整数倍,那么总大小就是12个字节,所以输出结果是12。
再来看一下图解
再来看一下s2
d是double型,占8个字节,对应到结构体变量偏移量为0 开始的地址处。
f是float型,占4个字节,对齐数就是4,对齐到4的整数倍位置处,即偏移量为8开始的地址处。
s1是结构体,占12个字节,对齐到自己的最大对齐数的整数倍处(即对齐数是4),对齐到4的整数倍的位置,即偏移量为12开始的地址处。
结构体总大小为最大对齐数的整数倍(包含s1的成员的最大对齐数),最大对齐数为8,故总大小是8的整数倍,那么总大小就是24个字节,所以输出结果是24。
图解如下图
分析一下s3的内存
s1是结构体,占12个字节,对应到结构体变量偏移量为0 开始的地址处。
s2是结构体,占24个字节,对齐数就是8,对齐到8的整数倍位置处,即偏移量为16开始的地址处。
str[10],是char类型,占10个字节,对齐数是1,对齐到1的整数倍的位置,即偏移量为40开始的地址处
结构体总大小为最大对齐数的整数倍(包含s1和s2的成员的最大对齐数),最大对齐数为8,故总大小是8的整数倍,那么总大小就是56个字节,所以输出结果是56。
3、结构体内存对齐的原因
- 平台原因(移植原因):
主要是由于不同的硬件平台对于不同数据类型的访问是有限制的。不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。因此,在这些平台上,结构体成员的布局必须满足一定的对齐要求,以确保访问这些成员时可以正常工作。
- 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。因此,通过对结构体进行内存对齐,可以减少处理器的访存时间,并提高程序的执行效率。