您的位置:首页 > 其它

内存中数据对齐(#pragma pack的用法)

2010-05-17 10:45 260 查看

结构体对齐的具体含义(#pragma pack的用法)

在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

 

更改C编译器的缺省字节对齐方式

在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:

     · 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。

     · 使用伪指令#pragma pack (),取消自定义字节对齐方式。

另外,还有如下的一种方式:

     · __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。

     · __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

 

 

朋友帖了如下一段代码:

  #pragma pack(4)

  class TestB

  {

  public:

    int aa;

    char a;

    short b;

    char c;

  };

  int nSize = sizeof(TestB);

  这里nSize结果为12(与没有用#pragma pack(4) 一样),在预料之中。

  现在去掉第一个成员变量为如下代码:

  #pragma pack(4)

  class TestC

  {

  public:

    char a;

    short b;

    char c;

  };

  int nSize = sizeof(TestC);

  结果显示nSize为6(与没有用#pragma pack(4) 一样),因为是按照short的size来对齐的。

 

 

关于struct的使用方法

struct是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如array、struct、union等)的数据单元。对于结构体,编译器会自动进行成员变量的对齐,以提高运算效率。缺省情况下,编译器为结构体的每个成员按其自然对界(natural alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

 自然对界是指按结构体的成员中size最大的成员对齐。

 

#pragma pack规定的对齐长度,实际使用的规则是:

结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和结构体的自然对齐长度中比较小的那个进行。

也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。

结构体的对齐,按照结构体中size最大的数据成员和#pragma pack指定值之间,较小的那个进行。

 

具体解释

#pragma pack(4)

  class TestB

  {

  public:

    int aa; //第一个成员,放在[0,3]偏移的位置,

    char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。

    short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。

    char c; //第四个,自身长为1,放在[8]的位置。

  };

这个类实际占据的内存空间是9字节

类之间的对齐,是按照类内部最大的成员的长度,和#pragma pack规定的值之中较小的一个对齐的。

所以这个例子中,类之间对齐的长度是min(sizeof(int),4),也就是4。

9按照4字节圆整的结果是12,所以sizeof(TestB)是12。

 

如果

#pragma pack(2)

class TestB

  {

  public:

    int aa; //第一个成员,放在[0,3]偏移的位置,

    char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。

    short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。

    char c; //第四个,自身长为1,放在[8]的位置。

  };

//可以看出,上面的位置完全没有变化,只是类之间改为按2字节对齐,9按2圆整的结果是10。

//所以 sizeof(TestB)是10。

最后看原贴:

现在去掉第一个成员变量为如下代码:

  #pragma pack(4)

  class TestC

  {

  public:

    char a;//第一个成员,放在[0]偏移的位置,

    short b;//第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。

    char c;//第三个,自身长为1,放在[4]的位置。

  };

//整个类的大小是5字节,按照min(sizeof(short),4)字节对齐
,也就是2字节对齐,结果是6

//所以sizeof(TestC)是6。

在Linux下面就是

#define __PACKED_ATTR __attribute__ ((__packed__))

 

typedef struct {

char p[3] __PACKED_ATTR;

long i __PACKED_ATTR;

} test ;

 

typedef struct {

char p[3];

long i;

} test1;

 

gcc test.c 编译后,它们的大小就是7,8了

 

 

windows下面默认的是#pragma pack(8)

因为编译器在编译时会对程序进行优化,以便加快访问速度,所以一般都会按照2的倍数进行字节对齐。用这个宏就是为了防止编译器对结构的定义进行对齐。

#pragma(push,n)用来设置警告消息的等级
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息