参考博客:
结构体对齐是为了更加快速的读取结构体中的变量。对齐之后,多余的内存空间被“填充”了,其中不放任何数据。
1、结构体的对齐规则
结构体的成员是按顺序排列在内存地址中的,根据结构体的对齐规则,结构体中的元素顺序可能会影响结构体整体占用的字节数。
规则一:结构体中的第一个成员位置在偏移量0,之后每个变量的偏移量必须是它本身字节数的整数倍。
规则二:如果结构体中嵌套结构体,那么嵌套的结构体的偏移量必须是它最大成员的字节数的整数倍。
规则三:结构体的总偏移量必须是它最大成员字节数的整数倍。
对于C++中的类也是同样的规则。
2、结构体的对齐实例
用sizeof计算各类型的字节数,得到结果如下表
char | 1 | short | 2 |
int | 4 | float | 4 |
long | 8 | double | 8 |
void* | 8 |
以下是求偏移量和长度的函数:
typedef unsigned int ui;
#define addr(x) (ui)(long)(x)
template <typename T>
void GetOffset(T t)
{
ui startAddr = addr(&t);
cout << "a: 0" \
<< "\tb: " << addr(&t.b)-startAddr \
<< "\tc: " << addr(&t.c)-startAddr \
<< "\td: " << addr(&t.d)-startAddr \
<< "\nlen: "<< sizeof(t) << endl;
}
(1)结构体s1
结构体s1的定义是:
struct s1{
int a; // 4
short b;// 2
long c; // 8
float d;// 4
};
计算的结果是:
s1
a: 0 b: 4 c: 8 d: 16
len: 24
存储空间表示如下:(2)结构体s2
结构体s2的定义是:
struct s2{
char a; // 1
char b; // 1
short c;// 2
long d; // 8
};
计算的结果是:
s2
a: 0 b: 1 c: 2 d: 8
len: 16
存储空间表示如下:(3)类s3
类s3的定义是:
class s3{
public:
char a; // 1
long b; // 8
short c; // 2
char d; // 1
};
计算的结果是:
s3
a: 0 b: 8 c: 16 d: 18
len: 24
存储空间表示如下:(4)类s4
类s4的定义是:
class s4{
public:
char a; // 1
char b; // 1
short c; // 4
long d; // 8
};
计算的结果是:s4
a: 0 b: 1 c: 2 d: 8
len: 16
存储空间表示如下:可以看出,类s3和类s4中的元素是一样的,只是顺序有些许差别,但因为对齐规则的存在,导致了两者所占用的空间完全不同。
(5)结构体s5
结构体s5的定义是:
struct s5{
int a; // 4
s4 b; // 16 max 8
char c; // 1
float d; // 4
};
这里的第二个量嵌套了类s4,根据之前的计算结果得知该类占用的空间为16字节,其中最大类型是8字节。
计算的结果是:
s5
a: 0 b: 8 c: 24 d: 28
len: 32
存储空间表示如下:3、指定对齐字节
对齐的指令为
#pragma pack(n)
或
#pragma pack(push, n)
作用是比较结构体中的最大宽度和指定的数,取其中较小的那个作为对齐字节数。
在计算结构体s1空间之前分别设定对齐字节数为4和16,则计算的结果分别是:
pack(4)
a: 0 b: 4 c: 8 d: 16
len: 20
pack(16)
a: 0 b: 4 c: 8 d: 16
len: 24
当对齐数为16时,因为最大宽度是8,小于16,所以存储方式不变;当对齐数设置为4时,小于最大宽度8,因而对齐数为4,存储方式如下图所示: