1. 程式人生 > >【轉】#pragma pack(push,1)與#pragma pack(1)的區別

【轉】#pragma pack(push,1)與#pragma pack(1)的區別

1 引子

      在程式中,有的時候我們定義結構體的時候,要用#pragma pack(push,1) & #pragma pack(pop)類似程式碼將結構體包起來。

一般形式如下:

#pragma pack(push,1);

struct A

{

 

} ;

#pragma pack(pop);

這麼做有什麼目的呢?

 注:下列內容來自網路。

2 #pragma pack簡介

#pragma pack是指定資料在記憶體中的對齊方式,

在C語言中,結構是一種複合資料型別,其構成元素既可以是基本資料型別(如int、long、float等)的變數,也可以是一些複合資料型別(如陣列、結構、聯合等)的資料單元。在結構中,編譯器為結構的每個成員按其自然對界(alignment)條件分配空間。各個成員按照它們被宣告的順序在記憶體中順序儲存,第一個成員的地址和整個結構的地址相同。

例 1:

struct sample
{
char a;
double b;
};

若不用#pragma pack(1)和#pragma pack()括起來,則sample按編譯器預設方式對齊(成員中size最大的那個)。即按8位元組(double)對齊,則sizeof(sample)==16.成員char a佔了8個位元組(其中7個是空位元組)

若用#pragma pack(1),則sample按1位元組方式對齊sizeof(sample)==9.(無空位元組)

例 2:下面的結構各成員空間分配情況:
struct test
{
     char x1;
     short x2;
     float x3;
     char x4;
};
     結構的第一個成員x1,其偏移地址為0,佔據了第1個位元組。第二個成員x2為short型別,其起始地址必須2位元組對界,因此,編譯器在x2和x1之間填充了一個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然對界地址上,在它們前面不需要額外的填充位元組。在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大對界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。

更改C編譯器的預設位元組對齊方式
     在預設情況下,C編譯器為每一個變數或是資料單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:
  · 使用偽指令#pragma pack (n),C編譯器將按照n個位元組對齊。
     · 使用偽指令#pragma pack (),取消自定義位元組對齊方式。

     另外,還有如下的一種方式:
     · __attribute((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。

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

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

3 應用例項

  在網路協議程式設計中,經常會處理不同協議的資料報文。一種方法是通過指標偏移的方法來得到各種資訊,但這樣做不僅程式設計複雜,而且一旦協議有變化,程式修改起來也比較麻煩。在瞭解了編譯器對結構空間的分配原則之後,我們完全可以利用這一特性定義自己的協議結構,通過訪問結構的成員來獲取各種資訊。這樣做,不僅簡化了程式設計,而且即使協議發生變化,我們也只需修改協議結構的定義即可,其它程式無需修改,省時省力。下面以TCP協議首部為例,說明如何定義協議結構。其協議結構定義如下:
 

 

#pragma pack(1) // 按照1位元組方式進行對齊

struct TCPHEADER
{
     short SrcPort; // 16位源埠號

     short DstPort; // 16位目的埠號

     int SerialNo; // 32位序列號

     int AckNo; // 32位確認號

     unsigned char HaderLen : 4; // 4位首部長度

     unsigned char Reserved1 : 4; // 保留6位中的4位

     unsigned char Reserved2 : 2; // 保留6位中的2位

     unsigned char URG : 1;
     unsigned char ACK : 1;
     unsigned char PSH : 1;
     unsigned char RST : 1;
     unsigned char SYN : 1;
     unsigned char FIN : 1;
     short WindowSize; // 16位視窗大小

     short TcpChkSum; // 16位TCP檢驗和

     short UrgentPointer; // 16位緊急指標

};

#pragma pack()



下面來自於另一個人的部落格:

這是編譯器的引數設定,有關結構體位元組對齊方式設定,#pragma pack是指資料在記憶體中的對齊方式。

#pragma pack (n)               作用:C編譯器將按照n個位元組對齊。

#pragma pack ()                作用:取消自定義位元組對齊方式。

#pragma pack(push,1)           作用:是指把原來的對齊方式壓棧,並設新的對齊方式設定為一個位元組對齊

#pragma pack(pop)                          作用:恢復對齊狀態

因此可見,加入push和pop可以使對齊恢復到原來狀態,而不是編譯器預設,可以說後者更優,但是很多時候兩者差別不大

如:

#pragma pack(push) //儲存對齊狀態

#pragma pack(4)//設定為4位元組對齊

  相當於 #pragma  pack (push,4)