C++ 字节对齐

折月煮酒 提交于 2019-12-23 03:52:00

1. 缘起

    来自BBS上的面试题目,struct{int a; char b;}的大小是多少?答案是8。上网看了下,是字节对齐。

2. 字节对齐的基本规则

    首先,每种类型的变量的默认对齐长度都是自己的变量长度,比如:char占一个字节,那么对齐长度就是一个字节,int占四个字节,对齐长度就是四个字节,double占八个字节,对齐长度就是8。int的对齐长度为4的实际意义是,int变量必须存储在四的倍数的地址上。
    那么对于struct{char b; int a},其长度是8,因为b虽然只占用1个字节,但是a必须从4的倍数开始存储,因此b后面的3个字节都废掉了。因此一共需要8个字节才能把b和a存下来。
    那么对于struct{int a; char b},其长度还是8!晕菜了!原因如下:
    字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
   1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
   2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
   3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
   规则1是控制结构体变量的首地址的,与结构体变量的长度没关系。
   规则2是控制结构体内每个变量的相对地址的,与结构体变量的长度有关系。
   规则3是控制结构体总体长度的,与结构体变量的长度有关系。
   正是由于第三条规则,结构体的长度必须是其最长的变量长度的整数倍,因此在上面的例子中,必须是4的整数倍,因此,是8。
   如果结构体里面嵌套结构体就要注意了,结构体变量的起始地址只是其内部最宽的基本类型的整数倍,而非结构体自身的整数倍,外面结构体的长度,也仅仅是里面最宽的基本类型的长度倍数。
   比如: 

struct S1 {
  
char c;  // 1个字节
  int i;   // 前面空3个字节,占用4个字节
}; // 刚好8个字节,是4的倍数 
struct S2 { 
  
char c1; // 1个字节
  S1 s;  // 前面空3个字节,而不是空7个字节,占用8个字节
  char c2; // 占用1个字节 
}; // 一共13个字节,要成为4的倍数,后面增加3个字节,成为16个字节

3. 字节对齐的原因

   ·效率原因,某些平台每次都是从偶地址读数据,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。

4. 字节对齐的隐患

   ·不合理设置变量定义的顺序,可能浪费内存   

struct S1 { // 长度为16
    char a;
    
int b;
    
char c;
    
int d;
}
struct S2 { // 长度为12
    char a;
    
char c;
    
int b;
    
int d;   
}

   ·更多的是在对地址运算时,没有考虑的字节对齐问题

char a;
int b;
int *pb = &a+1//这样a的地址加1,并不是b的地址,实际上,这行代码,在编译时就出现了ERROR,因为不能把char *的地址赋值给int *的变量

5. 参考文章

结构体字节对齐问题  http://blogold.chinaunix.net/u1/49467/showart_424793.html
字节的对齐原理 http://zhengyueyatou.blog.sohu.com/64814717.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!