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。
如果结构体里面嵌套结构体就要注意了,结构体变量的起始地址只是其内部最宽的基本类型的整数倍,而非结构体自身的整数倍,外面结构体的长度,也仅仅是里面最宽的基本类型的长度倍数。
比如:
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. 字节对齐的隐患
·不合理设置变量定义的顺序,可能浪费内存
char a;
int b;
char c;
int d;
}
struct S2 { // 长度为12
char a;
char c;
int b;
int d;
}
·更多的是在对地址运算时,没有考虑的字节对齐问题
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
来源:https://www.cnblogs.com/pangxiaodong/archive/2011/05/20/2052049.html