关于#pragma pack()字节对齐问题

来源:互联网 发布:好听的网络翻唱 编辑:程序博客网 时间:2024/04/29 03:48


个人在CentOS Linux 2.6.32(32位x86平台)上测试(gcc (GCC) 4.4.4 20100726 (Red Hat 4.4.4-13))


示例:

结构体定义一:

typedef struct AAA{        char a;——4        int c;———4        short b;——4        long long d;——8}AAA;
默认按4字节对齐,总共占用20字节长度;

结构体中char占用4个字节,因为后面的int需要按照4字节对齐,char和int之间空闲3个字节空间;


结构体定义二:

typedef struct AAA{        char a;——2        short b;——2        int c;——4        long long d;——8}AAA;
默认按4字节对齐,总共占用16字节;

与结构体一的不同在于,short需要按2字节对齐,所以在char和short直接有1字节的空闲空间


结构体示例三:

#pragma pack(1)typedef struct AAA{        char a;——1        short b;——2        int c;——4        long long d;——8}AAA;
按照1字节对齐的,结构体占用15个字节;


结构体示例四:

#pragma pack(8)typedef struct AAA{        char a;——2        short b;——2        int c;——4        long long d;——8}AAA;
#pragma pack(8)typedef struct AAA{        char a;——4        long long d;——8        short b;——4        int c;——4}AAA;

第一个结构体占用16字节,第二个占用20字节;

在32位机器,gcc编译仍然以4字节对齐,即设置高于4对齐的,均以4字节对齐方式;


结构体示例五:

typedef struct AAA{        char a;——2        short b;——2        int c;——4        long long d;——8}AAA;typedef struct BBB{        char a;——4        AAA d;——16        short b;——4        int c;——4}BBB;printf("sizeof(AAA)=%d\nsizeof(BBB)=%d\n", sizeof(AAA), sizeof(BBB));
上述结构体B的大小为28字节,虽然B结构体内的第一个char与A结构体内的第一个char紧邻,但两个char地址不紧邻;

结构体套结构体时,结构体采用当前模数(这里是4字节)对齐,而与结构体套用后,两种类型是否邻接没有关系;







下面是网上搜到的一些介绍:


一、内存对齐的原因

内存对齐(3张)
  大部分的参考资料都是如是说的:

  1、平台原因(移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

  2、性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。  


二、对齐规则

  每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

规则:

  1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

  2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

  3、结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。


另一种说法:

内存对齐原则:
一、结构体变量的首地址能够被其最宽基本类型成员大小与对齐基数中的较小者所整除;
二、结构体每个成员相对于结构体首地址的偏移量(offset)都是该成员大小与对齐基数中的较小者的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
三、结构体的总大小为结构体最宽基本类型成员大小与对齐基数中的较小者的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。




http://blog.csdn.net/jamesf1982/article/details/4375719

#pragma pack

作用:
指定结构体、联合以及类成员的packing alignment;

语法:
#pragma pack( [show] | [push | pop] [, identifier], n )

说明:
1,pack提供数据声明级别的控制,对定义不起作用;
2,调用pack时不指定参数,n将被设成默认值;
3,一旦改变数据类型的alignment,直接效果就是占用memory的减少,但是performance会下降;

语法具体分析:
1,show:可选参数;显示当前packing aligment的字节数,以warning message的形式被显示;
2,push:可选参数;将当前指定的packing alignment数值进行压栈操作,这里的栈是the internal compilerstack,同时设置当前的packing alignment为n;如果n没有指定,则将当前的packing alignment数值压栈;
3,pop:可选参数;从internal compilerstack中删除最顶端的record;如果没有指定n,则当前栈顶record即为新的packingalignment数值;如果指定了n,则n将成为新的packing aligment数值;如果指定了identifier,则internalcompilerstack中的record都将被pop直到identifier被找到,然后pop出identitier,同时设置packingalignment数值为当前栈顶的record;如果指定的identifier并不存在于internal compilerstack,则pop操作被忽略;
4,identifier:可选参数;当同push一起使用时,赋予当前被压入栈中的record一个名称;当同pop一起使用时,从internalcompilerstack中pop出所有的record直到identifier被pop出,如果identifier没有被找到,则忽略pop操作;
5,n:可选参数;指定packing的数值,以字节为单位;缺省数值是8,合法的数值分别是1、2、4、8、16。

重要规则:
1,复杂类型中各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个类型的地址相同;
2,每个成员分别对齐,即每个成员按自己的方式对齐,并最小化长度;规则就是每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中较小的一个对齐;
3,结构、联合或者类的数据成员,第一个放在偏移为0的地方;以后每个数据成员的对齐,按照#pragmapack指定的数值和这个数据成员自身长度两个中比较小的那个进行;也就是说,当#pragmapack指定的值等于或者超过所有数据成员长度的时候,这个指定值的大小将不产生任何效果;
4,复杂类型(如结构)整体的对齐是按照结构体中长度最大的数据成员和#pragma pack指定值之间较小的那个值进行;这样在成员是复杂类型时,可以最小化长度;
5,结构整体长度的计算必须取所用过的所有对齐参数的整数倍,不够补空字节;也就是取所用过的所有对齐参数中最大的那个值的整数倍,因为对齐参数都是2的n次方;这样在处理数组时可以保证每一项都边界对齐;


更改c编译器的缺省字节对齐方式:
在缺省情况下,c编译器为每一个变量或数据单元按其自然对界条件分配空间;一般地可以通过下面的两种方法来改变缺省的对界条件:
方法一:
使用#pragma pack(n),指定c编译器按照n个字节对齐;
使用#pragma pack(),取消自定义字节对齐方式。

方法二:
__attribute(aligned(n)),让所作用的数据成员对齐在n字节的自然边界上;如果结构中有成员的长度大于n,则按照最大成员的长度来对齐;
__attribute((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。