什么是字节对齐?
计算机并非逐字节读取内存,而是按2,4,8的倍数的字节块读写内存,故地址必须为上述倍数,故各种数据类型需要按照一定规则在空间上排列
对齐准则
- 结构体变量首地址可被对齐字节数大小所整除
- 结构体每个成员相对该结构体首地址的偏移都是成员大小的整数倍(如需要,填充字节)
- 结构体总大小为结构体对齐字节数大小的整数倍
为什么要字节对齐?
可优化【读写效率】【空间存储】【跨平台通信】
读写效率
考虑场景如下:若计算机每次读取8字节块,此时读取一个double,若未字节对其,可能该double位与两个不同的字节块,需要做两次读取才能读出double的值,显然效率低下!
空间存储
字节对齐的细节由编译器实现,为何我们还需要考虑字节对齐?
考虑如下代码
#include<stdio.h>
#include<stdint.h>
#ifdef DEBUG
struct test
{
int a;
char b;
int c;
short d;
test(int v1, char v2, int v3, short v4) :
a(v1),b(v2),c(v3),d(v4){}
}; /* 16字节 */
#else
struct test
{
int a;
char b;
short d;
int c;
test(int v1, char v2, int v3, short v4) :
a(v1),b(v2),c(v3),d(v4){}
};/* 12字节 */
#endif
int main(int argc, char* argv)
{
/*在32位和64位的机器上,size_t的大小不同*/
struct test a(1,'a',2,3);
printf("the size of struct test is %zu\n", sizeof(a));
return 0;
}
实验结果如下:
- 16字节内存分布:
- 12字节内存分布:
结论
设计结构体时,合理调整成员的位置,可大大较少存储空间(比如说将内存占用较小的变量顺序放置)
/* 32位机器上执行运行 */
struct test
{
int a;
char b;
int c;
short d;
}; /* 大小为16字节 */
/* 未对齐 : 0-3 , 4 , 5-8 , 9-10*/
/* 对齐: 0-3 , 4-7 , 8-11 , 12-15*/
struct test{
int a;
char b;
short d;
int c;
};
/* 对齐:0-3 , 4-5 , 6-7 , 8-11 */
/* b和d 存储在了同一字节块 */
跨平台通信
由于不同平台对齐方式可能不同,故可采用两种处理方式。
- 1字节对齐
- 主动对结构【字节填充】
1字节对齐
#pragma pack(n)
其内存分布如下(占11字节):#pragma pack(1) /*1字节对齐*/
struct test
{
int a;
char b;
int c;
short d;
};
#pragma pack()/*还原默认对齐*/
主动填充
不节省空间且扩展性差,不推荐struct test
{
int a;
char b;
char reserve[3];
int c;
short d;
char reserve1[2];
};
总结
在编程过程中,不需要关注字节对齐的细节,但不可忽略字节对齐,故总结了如下经验:
- 结构体合理安排成员位置
- 跨平台数据可考虑1字节填充
- 本地数据采用默认对齐,以提高访问效率