前段时间参加一个模拟面试的时候碰到过一个内存对齐的选择题,我本来一直以为我对这一块理解的很好,不过最后却挂掉了。
总结下来,就两个原则:
1:大小为size的子段,它的结构体内偏移offset需要符合offset mod size==0
2:整个结构体的大小必须为结构体内最大字段类型的整数倍
3:即使有结构体嵌套,内层结构体也需要和最长的标量对齐
可能说的有点模糊,不过下面我举几个例子你就明白了。看下面一段代码:
#include <iostream>
#include <cstdio>
struct Fuck1{
double a;
char b;
int c;
short d;
}ex1;
struct Fuck2{
char a;
short b;
int c;
}ex2;
struct Fuck3{
char a;
int c;
short b;
int *p;
}ex3;
struct Fuck4{
int a;
int b;
int c;
}ex4;
struct Fuck5{
struct Fuck4 ivec;
double a;
int b;
char c;
}ex5;
int main(void){
std::cout<<"64bit machine, pointer:"<<sizeof(long double*)<<std::endl;
std::cout<<"ex1:"<<sizeof(ex1)<<std::endl;
std::cout<<"ex2:"<<sizeof(ex2)<<std::endl;
std::cout<<"ex3:"<<sizeof(ex3)<<std::endl;
std::cout<<"ex4:"<<sizeof(ex4)<<std::endl;
std::cout<<"ex5:"<<sizeof(ex5)<<std::endl;
printf("%p\n",&ex5);
printf("%p\n",&(ex5.a));
return 0;
}
我先把运行结果贴出来,然后一个一个对应我前面提到的3个原则进行分析。运行结构如下(64位机器上指针大小为8byte):
在我的机器上size大小为4
OK,先看第一个24字节
struct Fuck1{
double a; //8byte
char b; //1byte
int c;
short d;
}ex1;
OK,先看第一句话:偏移量offset必须满足offset%4==0。假如说ex1起始地址为0,那么c的起始位置就必须为12,而不是a+b=8byte+1byte=9,因为要满足offset%4==0。
为什么要满足这一点呢?这个说起来其实很麻烦,不过核心就一句话 ,为了提高IO的存取速度。假使说ex1中变量的分布如下:
double a //address 0-7
char b //address 8
int c //9-12
short d //13-14
上述是没有内存对齐的情况,c的起始地址为9,那么这样机会出现麻烦,CPU需要进行两次IO操作才能把c中的数据读完。因为CPU的size为4,所以只能先读取8-11,然后12-15。所以如果我们在这里填充一下,如下;
double a //address 0-7
char b //address 8-11
int c //address 12-15
short //address 16-20
此时C位于起始地址为12的地方,这个时候CPU只需要一次就能把C读完了。
这也就是第一句话:结构体内偏移量 offset,需要满足offset%size==0
现在看第二个例子:
struct Fuck2{
char a;
short b;
int c;
}ex2;
ex2的大小实际为8,这个时候可能有人会认为整个结构体大小为12了,因为为每个都要满足offset%4==0啊,让我们再仔细考虑以下,char和short分别为1byte和2byte,所以a和b的大小总共为3byte,因为我一个字长为4byte,所以这两个就不要对齐了,反正我能一直把他们读完。
所以他们的真实情况是下面这样的:
char a //address 0
//这里填充了1byte,因为规定short类型的起始地址必须为偶数
short b //address 2-3
int c //address 4-7
下面看第三个例子:
struct Fuck3{
char a;
int c;
short b;
int *p;
}ex3;
ex3的大小为24字节,因为还要满足整个结构体的大小必须为结构体内最大子段的整数倍。ex3中最大子段大小为int*p,为8字节,所以整个结构体大小为24字节,而不是4(a)+4(b)+4(c)+8(p)=20字节。
现在看最后一个例子:
struct Fuck4{
int a;
int b;
int c;
}ex4;
struct Fuck5{
struct Fuck4 ivec;
double a;
int b;
char c;
}ex5;
这个大小为32字节,而不是36字节,虽然整个结构体内最大字段ex4大小为12字节 ,不过它要满足第三个原则。即使有结构体嵌套,它也要和最大字段也就是double a对齐。因此它的实际分布是下面这样:
Fuck4 ivec //0-15
double a //16-23
int b //24-27
char c //27-31
而实际上运行结果中的最后两行,也证明了我们的猜想。
以上就是全部内容:
参考资料:
https://github.com/ludx/The-Lost-Art-of-C-Structure-Packing
http://www.zhihu.com/question/27862634