C语言字节对齐

    xiaoxiao2021-03-25  132

    概念

    对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,则称作自然对齐。例如在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。

    字节对齐的例子

    数据类型自身的对齐值:对于char型数据,其自身对齐值为1;对于short型为2;对于int、float、double类型,其自身对齐值为4。 结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。 指定对齐值:#pragma pack (value)时的指定对齐值value。 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的值。

    查找与字节对齐方面的问题

    如果出现对齐或者赋值问题首先查看:

    编译器的大/小端设置; 看这种体系本身是否支持非对齐访问; 如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。
    转载请注明原文地址: https://ju.6miu.com/read-13280.html

    最新回复(0)