对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,则称作自然对齐。例如在32位CPU下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。
需要字节对齐的根本原因在于CPU访问数据的效率问题。假设上面整型变量的地址不是自然对齐,地址为0x00000002,则CPU获取该变量的值需要访问两次内存:第一次取从0x00000002-0x00000003的一个short;第二次取从0x00000004-0x00000005的一个short;然后组合得到所要的数据。如果变量在0x00000003地址上,则要访问三次内存,第一次为char;第二次为short;第三次为char;然后组合得到整型数据。而如果变量在自然对齐位置上,则只要一次就可以取出数据。
一些系统对对齐要求非常严格,比如sparc系统,如果取未对齐的数据会发生错误,举个例子:
char ch[8]; char *p = &ch[1]; int i = *(int *)p;运行时会报segment error,而在x86上就不会出现错误,只是效率下降。
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对齐条件:
使用伪指令#pragma pack指定对齐方式: #pragma pack (n),C编译器将按照n个字节对齐。 #pragma pack (),取消自定义字节对齐方式。 使用__attribute((aligned (n)))指定结构体的对齐方式: __attribute((aligned (n))) 让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。 __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,相当于aligned(1)。在设计不同CPU下的通信协议时,或者编写硬件驱动程序时寄存器的结构都需要按一字节对齐。即使看起来本来就自然对齐的也要使其对齐,以免不同的编译器生成的代码不一样。
例子1:
//默认4字节对齐 struct test { char x1; //1 byte //offset =0 short x2; //2 byte //offset =2 float x3; //4 byte //offset =4 char x4; //1 byte //offset =8 };//12 byte例子2:
//让编译器对这个结构作1字节对齐 #pragma pack(1) struct test { char x1; //1 byte //offset=0 short x2; //2 byte //offset=1 float x3; //4 byte //offset=3 char x4; //1 byte //offset=7 };//8 byte #pragma pack() //取消1字节对齐,恢复为默认4字节对齐例子3:
#define GNUC_PACKED __attribute__((packed)) struct PACKED test { char x1; short x2; float x3; char x4; }GNUC_PACKED; //这时候sizeof(struct test)的值仍为8。如果出现对齐或者赋值问题首先查看:
编译器的大/小端设置; 看这种体系本身是否支持非对齐访问; 如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。