关于内存对齐

来源:互联网 发布:丹道 知乎 编辑:程序博客网 时间:2024/06/07 09:09

前段时间参加一个模拟面试的时候碰到过一个内存对齐的选择题,我本来一直以为我对这一块理解的很好,不过最后却挂掉了。
总结下来,就两个原则:
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-7char b     //address 8int c      //9-12short d    //13-14

上述是没有内存对齐的情况,c的起始地址为9,那么这样机会出现麻烦,CPU需要进行两次IO操作才能把c中的数据读完。因为CPU的size为4,所以只能先读取8-11,然后12-15。所以如果我们在这里填充一下,如下;

double a    //address 0-7char b      //address 8-11int c       //address 12-15short       //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-3int 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-15double a      //16-23int b         //24-27char c        //27-31

而实际上运行结果中的最后两行,也证明了我们的猜想。
以上就是全部内容:
参考资料:
https://github.com/ludx/The-Lost-Art-of-C-Structure-Packing
http://www.zhihu.com/question/27862634

0 0
原创粉丝点击