字节对齐
字节对齐主要是为了提高内存的访问效率,比如intel 32位cpu,每个总线周期都是从偶地址开始读取32位的内存数据,如果数据存放地址不是从偶数开始,则可能出现需要两个总线周期才能读取到想要的数据,因此需要在内存中存放数据时进行对齐。各种类型数据需要按照一定的规则在内存中存放,而不是完全按字节挨字节
的顺序存放。每种数据类型的默认对其长度依赖于编译器的具体实现,不同的编译器可能有所不同。大多数情况下,基本数据类型的对其长度就是自己数据类型所占空间大小 (sizeof值)
。
对于结构体数据成员,默认的字节对齐一般满足以下几个准则。
- 结构体变量的首地址能够被其最宽数据类型成员大小整除
- 结构体每个成员相对结构体首地址的偏移量都是该成员本身大小的整数倍,如有需要会在成员之间填充字节。(0 被认为是任何数的整数倍)
- 结构体变量所占总空间的大小必定是最宽数据类型的整数倍。如有需要会再最后一个成员末尾填充若干字节,使得结构体所占空间大小是最宽数据类型大小的整数倍。
- union 成员取最大的成员的字节数作为其大小
- 由于结构体类型需要考虑到字节对齐的情况,所以不同的成员声明顺序会影响结构体的大小。
下面来看一个例子
struct A{
char a;
int b;
short c;
};
int main()
{
struct A a;
printf("sizeof(a):%d\r\n",sizeof(a));
return 0;
}
程序输出是sizeof(a):12
如果默认的对齐大小是4,结构体a的起始地址为0x0000,能够被最宽的数据成员大小(这里是int, 大小为4,有效对齐大小也是4)整除,故char a的从0x0000开始存放占用一个字节即0x0000 ~ 0x0001,然后是int b,其大小为4,故要满足第二个原则,需要从0x0004开始,所以在char a后填充三个字节,因此a对齐后占用的空间是0x0000 ~ 0x0003,b占用的空间是0x0004~0x0007, 然后是short c其大小是2,故从0x0008开始占用两个字节,即0x0008~0x0009。 此时整个结构体占用的空间是0x0000~0x0009, 占用10个字节,10%4 != 0, 不满足第三个原则,所以需要在后面补充两个字节,即最后内存对齐后占用的空间是0x0000~0x000B,一共12个字节。
内存中的分布是这样的:
使用#pragma pack (n)指定对齐n个字节