Data Alignment

    技术2022-05-11  18

    #pragma pack

     

    Preface

        很多 CPU ,如基于 Alpha, IA-64, MIPS, SuperH 体系的,拒绝读取未对齐数据。当一个程序要求其中之一的 CPU 读取未对齐数据时,这时 CPU 会进入异常处理状态并且通知程序不能继续执行。举个例子,在 ARM, MIPS, SH 硬件平台上,当操作系统被要求存取一个未对齐数据时默认通知应用程序一个异常。 对齐性        对齐性是一种内存地址的特性,表现为内存地址模上 2 的幂。例如,内存地址 0x0001103F 4 结果为 3 ;这个地址就叫做与 4n 3 对齐, 4 指出了所选用的 2 的幂的值。内存地址的对齐性取决于所选择的关于 2 的幂值。同样的地址模 8 结果为 7        一个内存地址符合表达式 Xn 0 ,那么就说该地址对齐于 X        CPU 执行指令就是对存储于内存上的数据进行操作,这些数据在内存上是以地址为标记的。对于地址来说,单独的数据会有其占用内存的字节数。如果它的地址对齐于它的字节数,那么就称作该数据自然对齐,否则称为未对齐。例如,一个标记 8 字节的浮点数据的地址对齐于 8 ,那么这个数据就自然对齐。 数据对齐性的编译处理        设备编译器以一种防止造成数据未对齐的方式来对数据进行地址分配。        对于单个的数据类型,编译器为其分配的地址是数据类型字节数的倍数。因此,编译器分配给 long 型变量的地址为 4 的倍数,就是说以 2 进制表示地址的话,最后两位为 0        另外,编译器以一种自然对齐每个结构成员的方式来填充结构体。参看下面的代码里面的结构体 struct x_ 。     struct x_      {            char a;     // 1 byte            int b;      // 4 bytes            short c;    // 2 bytes            char d;     // 1 byte      } MyStruct;      编译器填充这个结构以使其自然对齐。

    DataAlignment Phenomenon

    struct A {char a; long b; char c; long d;};

    struct B {char a; char c; long b; long d;};

    #progma pack(push,1)struct C {char a; long b; char c; long d;};#progma pack(pop)

    sizeof(A) = 16sizeof(B) = 12sizeof(C) = 10

    Description

    The #pragma pack directive modifies the current alignment rule for members of structures following the directive.

     

    Syntax

    where:

    1 | 2 | 4 | 8 | 16 Members of structures are aligned on the specified byte-alignment, or on their natural alignment boundary, whichever is less. nopack No packing is applied, and "nopack" is pushed onto the pack stack pop The top element on the pragma pack stack is popped. (no argument specified) Specifying #pragma () has the same effect as #pragma (pop).

     

    Notes

    The #pragma pack directive modifies the current alignment rule for only the members of structures whose declarations follow the directive. It does not affect the alignment of the structure directly, but by affecting the alignment of the members of the structure, it may affect the alignment of the overall structure according to the alignment rule.

    The #pragma pack directive cannot increase the alignment of a member, but rather can decrease the alignment. For example, for a member with data type of integer (int), a #pragma pack(2) directive would cause that member to be packed in the structure on a 2-byte boundary, while a #pragma pack(4) directive would have no effect.

    The #pragma pack directive is stack based. All pack values are pushed onto a stack as the source code is parsed. The value at the top of the current pragma pack stack is the value used to pack members of all subsequent structures within the scope of the current alignment rule.

    A #pragma pack stack is associated with the current element in the alignment rule stack. Alignment rules are specified with the -qalign compiler option or with the #pragma options align directive. If a new alignment rule is created, a new #pragma pack stack is created. If the current alignment rule is popped off the alignment rule stack, the current #pragma pack stack is emptied and the previous #pragma pack stack is restored. Stack operations (pushing and popping pack settings) affect only the current #pragma pack stack.

    The #pragma pack directive causes bitfields to cross bitfield container boundaries.

     

    Examples

    In the code shown below, the structure S2 will have its members packed to 1-byte, but structure S1 will not be affected. This is because the declaration for S1 began before the pragma directive. However, since the declaration for S2 began after the pragma directive, it is affected. struct s_t1 { char a; int b; #pragma pack(1) struct s_t2 { char x; int y; } S2; char c; int b; } S1; This example shows how a #pragma pack directive can affect the size and mapping of a structure: struct s_t { char a; int b; short c; int d; }S; Default mapping: With #pragma pack(1): sizeof S = 16 sizeof S = 11 offsetof a = 0 offsetof a = 0 offsetof b = 4 offsetof b = 1 offsetof c = 8 offsetof c = 5 offsetof d = 12

    offsetof d = 7

    align of a = 1 align of a = 1 align of b = 4 align of b = 1 align of c = 2 align of c = 1 align of d = 4

    align of d = 1

     

    (2007.10.15)请注意上文中红线所示部分,现补充解释如下:

    #pragma pack(8)

    struct s1{               short a;               long b;};

    struct s2{              char c;              s1 d;              long long e;};

    #pragma pack()

    sizeof(S2)结果为24.成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.S1中,成员a是1字节默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;S2中,c和S1中的a一样,按1字节对齐,而d 是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4.所以,成员d就是按4字节对齐.成员e是8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节的空,从第16个字节开始放置成员e.这时,长度为24,已经可以被8(成员e按8字节对齐)整除.这样,一共使用了24个字节.                          a    bS1的内存布局:11**,1111,                          c    S1.a S1.b     dS2的内存布局:1***,11**,1111,****11111111

    这里有三点很重要:1.每个成员分别按自己的方式对齐,并能最小化长度2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐

    补充一下,对于数组,比如:char a[3];这种,它的对齐方式和分别写3个char是一样的.也就是说它还是按1个字节对齐.如果写: typedef char Array3[3];Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度.不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个.

     

    最新回复(0)