之前一直觉得字节对齐是个症结,平时的实际应用中接触的太多了,如果不去弄懂它,感觉它就是一个短板,于是着手开始研究了它,今天在公司的例行讲解中又讲到了这个,回来想想还是整理下,算是给一个总结归纳。
首先我不得不提一个预编译命令 #pragma pack(),为了更好的切入主题我暂且只关心#pragma pack(n)这种设置当前字节对齐值为n的用法,有一点必须注意,n只有等于1,2,4,8,16才是有效值,其它的设置都是无效的。
下面,我从结构体入手,先看一段代码
#include <stdio.h>
#pragma pack (1)
struct stu1
{
int b;
};
#pragma pack ()
struct stu2
{
char a;
struct stu1 b;
};
int main()
{
printf("sizeof(struct stu1)=%d\nsizeof(struct stu2)=%d\n", sizeof(struct stu1),sizeof(struct stu2));
return 1;
}
运行结果
对于结构体stu1来说,它的字节对齐是1,是不是觉得很奇怪,通常我们查资料的时候都会说结构体的字节对齐值取的是结构体中所有类型中字节对齐值最大的那个,那如果这样的话,stu1难道不应该是4吗?变量a的后面不用补零吗?
问题进行到这里,我们有必要弄懂两个概念: 有效字节对齐值 和 类型自身字节对其值
int 型在32位系统上所占字节数为4,字节对齐值为4,即int型自身字节对齐值为4;类似的还有char、double等等;
struct结构体的自身字节对齐值为结构体中所有类型里自身字节对齐值最大的那个;
有效字节对齐值,其实是在最终实际内存操作中字节对齐的值,取的值是系统指定值(或默认值)与类型自身字节对齐值中 较小 的那个。如上例通过#pragma pack (1)指定字节对齐值为1,而结构体自身的字节对齐值为4(取int的),于是stu1的有效字节对齐值为1,所以在结构体stu2中,stu1相对于stu2结构体的首地址偏移量(变量a所占的内存大小)除以stu1的有效字节对齐值是整数,符合条件,故而不用在变量a之后补0,结果为5.
弄清楚了这些概念,那么我们再回顾一下结构体内存分配的原则:
1. 结构体内部,每个变量相当于结构体首地址的偏移量的大小必须是这个变量类型的有效字节对齐值的整数倍。
2. 结构体自身,结构体分配内存的大小必须是结构体有效字节对齐值的整数倍。
好了,弄清楚了这些概念,我们再开始看一段程序,来验证一下这些结论
#include <stdio.h>
#pragma pack (2)
struct stu1
{
char a;
int b;
};
#pragma pack ()
struct stu2
{
char a;
int b;
};
int main()
{
printf("sizeof(struct stu1)=%d\nsizeof(struct stu2)=%d\n",
sizeof(struct stu1),sizeof(struct stu2));
return 1;
}
运行结果
分析
变量 a b
stu1内存布局 *0 ****
stu2内存布局 *000 **** (*表示a、b的实际值,0表示为了字节对齐补的0值)