1. 程式人生 > >#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack()

#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack()

show 保持 數據 ide inter 同時 sta 數值 war

我們知道結構體內存對齊字節可以通過#pragma pack(n) 的方式來指定。

但是,有沒有想過一個問題,某些時候我想4字節對齊,有些時候我又想1字節或者8字節對齊,那麽怎麽解決這個問題呢?

此時,#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack()應運而生。

看測試代碼:說明,64位GCC,默認8字節對齊)

技術分享

屏蔽了的代碼選別看,只看這個結構體,在默認8字節對齊的方式下,sizeof大小為24個字節,這不再做分析,之前隨筆分析過了。

然後我加上強制4字節對齊之後:

技術分享

那麽現在,我再新建一個結構體B,內容和結構體C一樣,只是對齊方式向分別采取不同的方式:

#include <stdio.h>

#pragma   pack(4) 
struct C {
    double d;
    char b;
    int a;
    short c;
};
#pragma pack() 
struct B {
    double d;
    char b;
    int a;
    short c;
};

像上面那樣處理之後,輸出:先打印結構C,再打印結構B

技術分享

這說明了,在強制4字節對齊之後,我加上#pragma pack() ,能夠讓程序恢復默認對齊(這裏是8字節)狀態。

#pragma pack() 能夠取消自定義的對齊方式,恢復默認對齊。

繼續測試:

#pragma   pack(4) 
struct CC {
    double d;
    char b;
    int a;
    short c;
};
#pragma pack(pop) 
struct BB{
    double d;
    char b;
    int a;
    short c;
};

輸出:

技術分享

好像沒什麽作用的感覺,那麽再加上一個#pragma pack(push)試試呢?

#include <stdio.h>

#pragma pack(push) 
#pragma   pack(4) 
struct CC {
    
double d; char b; int a; short c; }; #pragma pack(pop) struct BB{ double d; char b; int a; short c; }; int main(void) { printf("%u\n%u\n",sizeof(struct CC),sizeof(struct BB)); return 0; }

技術分享

這樣似乎改變了,有不同的地方體現了出來。

#pragma pack(push):

英文單詞push是“壓入”的意思。編譯器編譯到此處時將保存對齊狀態(保存的是push指令之前的對齊狀態)。

#pragma pack(pop):

英文單詞pop是”彈出“的意思。編譯器編譯到此處時將恢復push指令前保存的對齊狀態(請在使用該預處理命令之前使用#pragma pack(push))。

push和pop是一對應該同時出現的名詞,只有pop沒有push不起作用,只有push沒有pop可以保持之前對齊狀態(但是這樣就沒有使用push的必要了)。

這樣就可以知道,當我們想要一個結構體按照4字節對齊時,可以使用#pragma pack(4) ,最後又想使用默認對齊方式時,可以使用#pragma pack() ;

也可以使用:

#pragma pack(push)
#pragma pack(4)

struct。。。

#pragma pack(pop)

這樣在push和pop之間的結構體就可以按照pack指定的字節(這裏是4字節對齊方式),而pop之後的結構體按照#pragma pack(push) 前對齊方式。

eg:

#include <stdio.h>
#pragma   pack(2) 
#pragma pack(push) 
#pragma   pack(4) 
struct CC {
    double d;
    char b;
    int a;
    short c;
};
 
#pragma   pack(1) 
struct BB{
    double d;
    char b;
    int a;
    short c;
};
#pragma pack(pop)
struct AA{
    double d;
    char b;
    int a;
    short c;
};
int main(void)
{
    
    printf("%u\n%u\n%u\n",sizeof(struct CC),sizeof(struct BB),sizeof(struct AA));
    return 0;
}

技術分享

先按照2字節對齊,然後push保存2字節對齊,然後有強制4字節對齊,打印CC為20字節,然後強制1字節對齊,打印BB為15字節,然後pop,pop會讓編譯器回到push之前的對齊方式(這裏是2字節對齊),打印AA(按照2字節對齊)16字節。

註意,#pragma pack() 取消自定義對齊方式,恢復默認方式,而push之後pop是回到push指令之前的對齊方式。

eg:

#include <stdio.h>
#pragma   pack(2) 
#pragma pack(push) 
#pragma   pack(4) 
struct CC {
    double d;
    char b;
    int a;
    short c;
};
 
#pragma   pack(1) 
struct BB{
    double d;
    char b;
    int a;
    short c;
};
#pragma pack()
struct AA{
    double d;
    char b;
    int a;
    short c;
};
int main(void)
{
    
    printf("%u\n%u\n%u\n",sizeof(struct CC),sizeof(struct BB),sizeof(struct AA));
    return 0;
}

只把pop改成pack()打印如下:

技術分享

最後回到的不是2字節對齊,而是默認的8字節對齊。

還有延伸點:

技術分享

上圖紅色處等價於下面屏蔽的兩句。

語法:
#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 compiler stack,同時設置當前的packing alignment為n;如果n沒有指定,則將當前的packing alignment數值壓棧;
3,pop:可選參數;從internal compiler stack中刪除最頂端的record;如果沒有指定n,則當前棧頂record即為新的packing alignment數值;如果指定了n,則n將成為新的packing aligment數值;如果指定了identifier,則internal compiler stack中的record都將被pop直到identifier被找到,然後pop出identitier,同時設置packing alignment數值為當前棧頂的record;如果指定的identifier並不存在於internal compiler stack,則pop操作被忽略;
4,identifier:可選參數;當同push一起使用時,賦予當前被壓入棧中的record一個名稱;當同pop一起使用時,從internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier沒有被找到,則忽略pop操作;
5,n:可選參數;指定packing的數值,以字節為單位;

另外:

__attribute(aligned(n))讓所作用的數據成員對齊在n字節的自然邊界上;如果結構中有成員的長度大於n,則按照最大成員的長度來對齊;
__attribute((packed))取消結構在編譯過程中的優化對齊,按照實際占用字節數進行對齊

#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack()