关于内存对齐

前段时间参加一个模拟面试的时候碰到过一个内存对齐的选择题,我本来一直以为我对这一块理解的很好,不过最后却挂掉了。
总结下来,就两个原则:
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):
关于内存对齐_第1张图片
在我的机器上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

你可能感兴趣的:(c,内存,对齐)