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

咱們知道結構體內存對齊字節能夠經過#pragma pack(n) 的方式來指定。ide

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

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

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

屏蔽了的代碼先別看,只看這個結構體,在默認8字節對齊的方式下,sizeof大小爲24個字節,這再也不作分析,以前隨筆分析過了。3d

而後我加上強制4字節對齊以後:code

那麼如今,我再新建一個結構體B,內容和結構體C同樣,只是對齊方式向分別採起不一樣的方式:orm

#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,再打印結構Bblog

這說明了,在強制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))取消結構在編譯過程當中的優化對齊,按照實際佔用字節數進行對齊

相關文章
相關標籤/搜索