C++

字节对齐

Posted by Hazuki on 2019-06-23

不同系统下C语言类型长度

Data Type ILP32 ILP64 LP64 LLP64
char 8 8 8 8
short 16 16 16 16
int 32 64 32 32
long 32 64 64 32
float 32 32 32 32
double 64 64 64 64
long long 64 64 64 64
pointer 32 64 64 64

大部分64位Unix, Linux 使用的都是LP64模型,32位Linux使用ILP32模型,64位Windows使用LLP64模型。

LP64即 long、pointer 为64位,ILP64即 int、long、pointer为64位,ILP32 即 int、long、pointer为32位,LLP64即 long long、pointer为64位。

字节对齐

字节对齐的细节和具体编译器实现相关,对于标准数据类型,其地址只需要为其长度的整数倍。其他数据类型,数组按照基本数据类型对齐,union按照其包含长度最大的数据类型对齐,当数据类型为结构体时,一般而言满足三个准则:

  • 结构体变量的首地址能够被其最宽基本类型成员的大小所整除
  • 结构体每个成员对于结构体首地址的偏移量都是成员大小的整数值,如有需要,编译器会在成员之间加上填充字节
  • 结构体的总大小为结构体最宽基本类型成员大小的整数倍。如有需要,编译器会在最末一个成员之后加上填充字节。

一个例子,定义如下的一个结构体

1
2
3
4
5
6
struct Test
{
int a_int;
double b_double;
float c_float;
}

输出结构体的大小,每一个成员的地址,结果如下:

1
2
3
4
5
6
7
size of int: 4
size of double: 8
size of float: 4
size of Test: 24
test.a_int: 0x61ff08
test.b_double: 0x61ff10
test.c_float: 0x61ff18

可以看出,int 和 double 的地址相差了8个字节,而为了使得结构体的大小为最宽成员大小的整数倍,编译器在float后面添加了4个字节。

当修改结构体为如下格式时:

1
2
3
4
5
6
struct Test2
{
int a_int;
float c_float;
double b_double;
}

结构体对应的大小及地址如下:

1
2
3
4
size of Test2: 16
Test2.a_int: 0x61ff10
Test2.c_float: 0x61ff14
Test2.b_double: 0x61ff18

可以看出,编译器没有添加填充字节,结构体的大小等于三个成员大小之和。

字节对齐的原因

保证CPU访问数据的效率。如果一个处理器每次能从存储器中读取8个字节,当一个double类型的数据地址为8的倍数时,就可以保证每次都能读取出这个double。否则,需要执行多次存储器访问,因为对象可能被分放在两个8字节存储块中。