1.字节对齐的概念
(1)背景
当系统为我们划分一段连续内存空间时,cpu在访问内存空间数据理论上时可以从任意地址访问随机大小的数据。但是由于硬件方面和操作系统的问题,
CPU在读写内存的时候是以内存块来读取内存的,所以如果在读取了一个int数据紧接着又读取一个char型数据,那么可能cpu会直接读取到更多的地址。
然后再重新定位到char的内存地址上。当然,这是在每个数据都连续内存存放的情况下,显然,CPU的访问效率会降低很多。
(2)什么是字节对齐
为了提高CPU的内存访问效率。于是引入了字节(内存)对齐的概念,毫无疑问,字节对齐是通过消耗内存空间来换取CPU效率的一个手段。
2.字节对齐的准则
(1)基本数据类型的自身对齐值
(2)程序的指定对齐值(#pragma pack(value)时的value)
(3)自定义类型的自身对齐值(即结构体或类成员自身对齐值最大的值)
(4)自定义类型的有效对齐值(自定义类型的自身对齐值和指定对齐中较小的值)
3.字节对齐实例
(1)普通结构体对齐
struct Test1 // 对齐情况下为24,不对齐情况下为13,本身对齐值为8
{
int a; // 4+4 4指定对齐值为4,自身对齐值为8
double b; // 8 8
char c; // 1+7 1+3
};
对于struct Test结构体,按照我们猜想,整个结构体在内存中所占字节应该是4+8+1=13,但是我们用printf("%d\n",sizeof(struct Test));输出24。
别忘了内存对我们的数据对齐了。由于我们没有对程序指定对齐值,那我们先看变量a,自身对齐值4bytes,变量b占用8bytes,这时候自身对齐值为8。
需要在a的4字节后面再填充4个字节。c占一个字节,后面没有数据。这个时候Test中成员所占内存空间就是4+4+8+1=17。
但是结构体本身需要按照最宽数据成员对齐值的整数倍对齐,所以struct Test的自身对齐值为7填充在c之后,struct Test的实际对齐值就是17+7=24。
(2)上述顺序调换
struct Test2 // 最终大小是自定义大小的整数倍
{
char c; // 1+3
int a; // 4
double b; // 8, 这个地方要看上面整体开辟空间是8的倍数字
};
和(1)一样只是把结构体成员的顺序做调换,c这个时候4Bytes,a也是4Bytes,b占8个bytes,整个Test结构体大小必须是8的倍数,现在就是1+3+4+8=16。
(3)更多普通结构体对齐实例
struct Test3 // 16
{
double a; // 8
char b; // 4
int c; // 4
};
struct Test4 // 3
{
char a; // 1
char b; // 1
char c; // 1
};
同理,Test3里面a占8个字节,b有1+3个字节,c有4个字节,结构体大小对齐8+4+4=16字节,不额外增加对齐值。而Test4中都是char类型,所以Test4大小3Bytes。
(4)我们可以发现在系统默认的存在字节对齐的概念下,让高字节的数据类型放在结构体后面总是能消耗较少的内存空间。