学习笔记|字节对齐


本文对字节对齐的原因是出于正确性或者效率方面不做讨论,只讨论C/C++中字节是如何对齐的。本文讨论基于Visual C++6.0,所有代码均在其上运行。
【学习笔记|字节对齐】本文参考网上其它文章,例子与其大同小异,如果您认为侵犯您的版权敬请告知予以更改。
首先看下面的程序:
struct A { int a; char b; short c; }; struct B { char b; int a; short c; };
对于32位的系统,char类型为1个字节、int类型为4个字节、short为2个字节,表面上看,sizeof(A)和sizeof(B)都应该是7个字节才对,但运行的结果呢?前者长度为8,后者长度为12。
之所以产生这样的结果,是因为默认情况下,编译器对结构的存储进行了对齐操作。
变量或者类型的对齐长度
对于基本类型,默认对齐长度即变量的字节数,如在32位系统中,char为1个字节、int为个字节;
对于结构体、联合体、和类等自定义类型,对齐长度为其内部成员的最大对齐长度,如上面的结构体A和B,其成员中a的对齐长度为4最大,则A和B的对齐长度即为4。
默认情况下,变量的存储首地址必须为其对齐长度的整数倍,如short为2个字节,则short类型的变量只能从偶数地址开始存储。
现在回头来看结构A和B的对齐,假使结构从地址0x0000处开始存储,对于结构A,第一个成员a为整形,占据0x0000~0x0003四个字节,第二个成员b为一个字节,存储在0x0004,第三个成员c占两个字节,必须从偶数开始存储,因此0x0005被跳过,c被存储在0x0006和0x0007中。因此sizeof(A)的结果为8;
对于结构B,第一个成员占1个字节,存储在0x0000中,第二个成员a为整形,4个字节,存储的起始地址必须为4的整数倍,因此0x0001~0x0003被跳过,a被存储在0x0004~0x0007中,第三个成员c占两个字节,存储在0x0008和0x0009中。从0x0000到0x0009应为10个字节,sizeof(B)的结果为什么会是12呢?
考虑如下的情形:
struct B bArray[10];
由于数组是按顺序存储的,第一个元素占据从0x0000到0x0009这十个字节,第二个元素存储的时候,由于结构B的对齐长度为4,因此必须从0x000C处开始存储,则0x000A和0x000B被闲置,数组存储将不再连续,因此从数组存储的角度,编译器将0x000A和0x000B保留在结构B中,如此以来,数组中元素的存储就连续了。也就是说,结构的长度必须为该结构对齐长度的整数倍。
改变默认对齐长度
#pragma pack(n)
#pragma pack()
来改变默认的对齐长度,n的取值为1、2、4、8和16,被称为有效对齐长度。但如果n大于结构的对齐长度将按默认的对齐长度存储。比如下面的例子:
#pragma pack(8)
struct C
{
char b;
int a;
short c;
};
#pragma pack()
因为n=8大于C的对齐长度4,则sizeof(C)的值为12而不是3*8=24。如果n=1,则sizeof(C)的值为7,n=2则为8。

此文档只讨论字节如何对齐,对于字节对齐的理由以及由字节对齐而引起的问题,百度一下。对于具体的硬件平台,字节对齐大部分已由编译器自动实现,不同的编译器实现的结果可能和本文说明的不一致,请参考具体平台的说明。

    推荐阅读