C語言的字節對齊及#pragma pack的使用

C編譯器的缺省字節對齊方式(自然對界)


在缺省情況下,C編譯器爲每一個變量或是數據單元按其自然對界條件分配空間。


在結構中,編譯器爲結構的每個成員按其自然對界(alignment)條件分配空間。各個成員按照它們被聲明的順序在內存中順序存儲(成員之間可能有插入的空字節),第一個成員的地址和整個結構的地址相同。




C編譯器缺省的結構成員自然對界條件爲“N字節對齊”,N即該成員數據類型的長度。如int型成員的自然對界條件爲4字節對齊,而double類型的結構成員的自然對界條件爲8字節對齊。若該成員的起始偏移不位於該成員的“默認自然對界條件”上,則在前一個節面後面添加適當個數的空字節。




C編譯器缺省的結構整體的自然對界條件爲:該結構所有成員中要求的最大自然對界條件。若結構體各成員長度之和不爲“結構整體自然對界條件的整數倍,則在最後一個成員後填充空字節。


例子1(分析結構各成員的默認字節對界條界條件和結構整體的默認字節對界條件):


struct Test
{
char x1; // 成員x1爲char型(其起始地址必須1字節對界),其偏移地址爲0
char x2; // 成員x2爲char型(其起始地址必須1字節對界,其偏移地址爲1
float x3; // 成員x3爲float型(其起始地址必須4字節對界),編譯器在x2和x3之間填充了兩個空字節,其偏移地址爲4
char x4; // 成員x4爲char型(其起始地址必須1字節對界),其偏移地址爲8
};


因爲Test結構體中,最大的成員爲flaot x3,因些此結構體的自然對界條件爲4字節對齊。則結構體長度就爲12字節,內存佈局爲1100 1111 1000。


例子2:


#include <stdio.h>
//#pragma pack(2)
typedef struct
{
  int aa1; //4個字節對齊 1111
  char bb1;//1個字節對齊 1
  short cc1;//2個字節對齊 011
  char dd1; //1個字節對齊 1
  } testlength1;
int length1 = sizeof(testlength1); //4個字節對齊,佔用字節1111 1011 1000,length = 12
typedef struct
{
  char bb2;//1個字節對齊 1
  int aa2; //4個字節對齊 01111
  short cc2;//2個字節對齊 11
  char dd2; //1個字節對齊 1
  } testlength2;
int length2 = sizeof(testlength2); //4個字節對齊,佔用字節1011  1111 1000,length = 12
typedef struct
{
  char bb3; //1個字節對齊 1
  char dd3; //1個字節對齊 1
  int aa3; //4個字節對齊 001111
  short cc23//2個字節對齊 11
  } testlength3;
int length3 = sizeof(testlength3); //4個字節對齊,佔用字節1100 1111 1100,length = 12
typedef struct
{
  char bb4; //1個字節對齊 1
  char dd4; //1個字節對齊 1
  short cc4;//2個字節對齊 11
  int aa4; //4個字節對齊 1111
  } testlength4;
int length4 = sizeof(testlength4); //4個字節對齊,佔用字節1111 1111,length = 8
int main(void)
{
  printf("length1 = %d.\n",length1);
  printf("length2 = %d.\n",length2);
  printf("length3 = %d.\n",length3);
  printf("length4 = %d.\n",length4);
  return 0;
}


改變缺省的對界條件(指定對界)

· 使用僞指令#pragma pack (n),C編譯器將按照n個字節對齊。

· 使用僞指令#pragma pack (),取消自定義字節對齊方式。



這時,對齊規則爲:


1、數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset爲0的地方,以後每個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中,比較小的那個進行。


2、結構(或聯合)的整體對齊規則:在數據成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行。


結合1、2推斷:當#pragma pack的n值等於或超過所有數據成員長度的時候,這個n值的大小將不產生任何效果。




因此,當使用僞指令#pragma pack (2)時,Test結構體的大小爲8,內存佈局爲11 11 11 10。



需要注意一點,當結構體中包含一個子結構體時,子結構中的成員按照#pragma pack指定的數值和子結構最大數據成員長度中,比較小的那個進行進行對齊。例子如下:


#pragma pack(8)

struct s1{

short a;

long b;

};



struct s2{

char c;

s1 d;

long long e;

};

#pragma pack()


sizeof(s2)的結果爲24。S1的內存佈局爲1100 1111,S2的內存佈局爲1000 1100 1111 0000 1111 1111。


例子:



#include <stdio.h>
#pragma pack(2)
typedef struct
{
  int aa1; //2個字節對齊 1111
  char bb1;//1個字節對齊 1
  short cc1;//2個字節對齊 011
  char dd1; //1個字節對齊 1
  } testlength1;
int length1 = sizeof(testlength1); //2個字節對齊,佔用字節11 11 10 11 10,length = 10
typedef struct
{
  char bb2;//1個字節對齊 1
  int aa2; //2個字節對齊 01111
  short cc2;//2個字節對齊 11
  char dd2; //1個字節對齊 1
  } testlength2;
int length2 = sizeof(testlength2); //2個字節對齊,佔用字節10 11 11 11 10,length = 10
typedef struct
{
  char bb3; //1個字節對齊 1
  char dd3; //1個字節對齊 1
  int aa3; //2個字節對齊 11 11
  short cc23//2個字節對齊 11
  } testlength3;
int length3 = sizeof(testlength3); //2個字節對齊,佔用字節11 11 11 11,length = 8
typedef struct
{
  char bb4; //1個字節對齊 1
  char dd4; //1個字節對齊 1
  short cc4;//2個字節對齊 11
  int aa4; //2個字節對齊 11 11
  } testlength4;
int length4 = sizeof(testlength4); //2個字節對齊,佔用字節11 11 11 11,length = 8
int main(void)
{
  printf("length1 = %d.\n",length1);
  printf("length2 = %d.\n",length2);
  printf("length3 = %d.\n",length3);
  printf("length4 = %d.\n",length4);
  return 0;
}


另外,還有如下的一種方式:


· __attribute((aligned (n))),讓所作用的結構成員對齊在n字節自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。


· __attribute__ ((packed)),取消結構在編譯過程中的優化對齊,按照實際佔用字節數進行對齊。



以上的n = 1, 2, 4, 8, 16... 第一種方式較爲常見。


轉自:http://www.cnblogs.com/dabiao/archive/2010/04/15/1712458.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章