技术群的筒子们有时候会提到结构体对齐,说实话这个问题还不是几句话能讲清楚的。这个问题网上一搜一大把,已经有无数的前辈总结过。看了很多网上的资料,根据我个人的一些理解,再总结一下,配了些图片,希望大家能看懂。
首先是结构体对齐规则:
1、对于n个字节的元素,它的首地址要能被n整除
eg:int—4个字节的元素,它为结构体的第一个元素时,起始地址应能被4整除
double—8个字节的元素,它为结构体的第一个元素时,起始地址应能被8整除
2、结构体成员在内存中顺序存放,所占内存地址依次增高,第一个成员处在低地址处,最后一个成员处在高地址处,但结构体成员之间的内存分配不一定是连续的
3、对待每个成员,类似于对待单个n字节元素一样,依次为每个元素找一个合适的首地址,使其符合第一条对齐原则
4、编译器可设置对齐参数X,但这个X并不是结构体成员的实际对齐参数,结构体中每个成员的对齐参数N这样计算
N= min(sizeof(成员类型),X)
5、整个结构体的长度必须是各成员所使用的对齐参数N中最大的那个值的整数倍
即总长度LEN= {max(N1,N2,……,N)}的最小整数倍
6、当结构体成为为数组时,将数组的每个元素当一个成员来看待
7、当结构体成为类结构类型时,那么该过程是个递归过程,且把该成员当成一个整体来看待
上面的规则有点晕,对照着看下面的例子
#pragma pack(8) //设置编译器以8字节对齐
struct A
{
char c; //①成员的对齐参数N1= min(sizeof(char) , 8) = 1
double d; //②成员的对齐参数N2 = min(sizeof(double) , 8) = 8
short s; //③成员的对齐参数N3 = min(sizeof(short) , 8) = 2
int i; //④成员的对齐参数N4 = min(sizeof(int) , 8) = 4
}
结构体的总成都LEN = {max(N1,N2,N3,N4)}的整数倍 ,所以长度LEN必须是8的整数倍,从下面的内存分布图中可看出,总长度是24字节
#pragma pack(4) //设置编译器以4字节对齐
struct A
{
char c; //①成员的对齐参数N1= min(sizeof(char) , 4) = 1
double d; //②成员的对齐参数N2 = min(sizeof(double) , 4) = 4
short s; //③成员的对齐参数N3 = min(sizeof(short) , 4) = 2
int i; /④成员的对齐参数N4 = min(sizeof(int) , 4) = 4
}
结构体的总成都LEN = {max(N1,N2,N3,N4)}的整数倍 ,所以长度LEN必须是4的整数倍,从下面的内存分布图中可看出,总长度是20字节
#pragma pack(2) //设置编译器以2字节对齐
struct A
{
char c; //①成员的对齐参数N1= min(sizeof(char) , 2) = 1
double d; //②成员的对齐参数N2 = min(sizeof(double) , 2) = 2
short s; //③成员的对齐参数N3 = min(sizeof(short) , 2) = 2
int i; //④成员的对齐参数N4 = min(sizeof(int) , 2) = 2
}
结构体的总成都LEN = {max(N1,N2,N3,N4)}的整数倍 ,所以长度LEN必须是2的整数倍,从下面的内存分布图中可看出,总长度是16字节
#pragma pack(1) //设置编译器以1字节对齐
struct A
{
char c; //①成员的对齐参数N1= min(sizeof(char) , 1) = 1
double d; //②成员的对齐参数N2 = min(sizeof(double) , 1) = 1
short s; //③成员的对齐参数N3 = min(sizeof(short) , 1) = 1
int i; //④成员的对齐参数N4 = min(sizeof(int) , 1) = 1
}
以上的例子都是在32位的系统上试的,这里两个结构体成员之间的“空隙”我用“cc”来填充,这纯粹是画起来方便,VS2010中这些“空隙”里的值都是0。
当然我之前在单步执行别的C程序的时候发现,很多初始化的内存、栈空间,都以"cc"字符来填充。最近在一本书上看到了解释,抄录在此:
对于X86处理器来说,0xcc是不错的填充数值,因为它是断点中断(int 03h)的机器码,如果你在调试器中运行程序并试图执行一段数据而不是执行代码时,就会陷入断点。
另外,使用0xcc的另一个好处是它可以很容易从内存转储的信息中识别出来