關於結構體內存對齊方式的總結(#pragma pack()和alignas())

最近閒來無事,翻閱msdn,在預編譯指令中,翻閱到#pragma pack這個預處理指令,這個預處理指令爲結構體內存對齊指令,偶然發現還有另外的內存對齊指令aligns(C++11),__declspec(align(#))(Microsoft專用),遂去探究二者之間的不一樣點。c++

一、#pragma packless

這個指令爲預處理指令,所謂與處理指令執行在程序的預處理階段,該指令對應着編譯選項/Zp,能夠在vs的工程屬性中設置編譯選項的內存對齊,也能夠利用預處理指令來設置。ide

#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n ) 
spa

預處理指令的用法如上,其中必選參數n爲內存對齊值,有效值爲一、四、八、16,默認值爲8。可選參數中,show表明右警告消息顯示當前的內存對齊值,push | pop 兩者選其一,表明將當前的內存對齊值入、出棧。identifier跟隨push | pop一同出入棧,做爲表示使用。c++11

二、aligns、__declspec(align(#))code

aligns和alignof爲c++標準的類型說明和運算符,其中aligns爲內存對齊類型符,alignof爲返回內存對齊值得運算符。對應還有Microsoft的msvc專用的__alignof()和__declspec(align(#))也能夠達到一樣的的效果。blog

__declspec(align(#))和aligns(#)中#爲對齊值,可選的對齊值爲二、四、八、1六、3二、64內存

三、區別ci

一樣是對齊操做,兩種不一樣的對齊操做有什麼區別呢。編譯

字面上,一個是預處理指令,另外一個是類型說明符。

在相應的結構體內存對齊上,也存在差異。

所謂的內存對齊指的是變量分配到要求的內存地址上,這個地址必須是對齊值的整數倍,例如int型變量,默認的對齊值爲數據長度,也就是4,分配到內存地址0x0001103F,該地址模上內存對齊值,也就是4。1103F%4=3,因此將int型變量分配到0x00011042上,該地址爲對齊值得整數倍。

定義一個結構體,結構體內存對齊值爲內存存儲變量的最大默認對齊值

 struct   MyStruct
    {
        char member;
        int a;

    }mystruct;

    sizeof(mystruct) = 8,alignof(MyStruct) = 4

經過計算能夠看出,結構體的對齊值爲4,內存佔用爲8。結構體遵循內存對齊規則,結構體內部變量也遵循對齊規則,其中char偏移量0,int偏移量爲4,兩變量之間填充3字節數據。

  • #pragma pack(1),再次計算sizeof和alignof

    sizeof(mystruct) = 5;alignof(MyStruct) = 1

    能夠看出,預處理指令要求的內存對齊爲1設置生效了。

  • __declspec(align(2)),最小對齊值,再次計算sizeof和alignof

    __declspec(align(2))
     struct   MyStruct
     {
         char member;
         int a;

     }mystruct;

    sizeof(mystruct) = 8;alignof(MyStruct) = 4

    設置內存對齊失敗了。

    •  用c++11標準再試試,align(2) 
struct  alignas(2)   MyStruct
     {
         char member;
         int a;

     }mystruct;

這下vs直接報錯,'MyStruct': Alignment specifier is less than actual alignment (4), and will be ignored

    經過這個報錯,能夠看出,類型說明符形式的內存對齊要求最小的對齊值爲結構體的對齊默認值,也就是內存變量的最大對齊  值。

  • __declspec(align(8)),計算sizeof和alignof
  __declspec(align(8))  struct   MyStruct
     {
         char member;
         int a;
     }mystruct;
     sizeof(mystruct) = 8;alignof(MyStruct) = 8

     struct alignas(8)  MyStruct
     {
         char member;
         int a;

     }mystruct;

  sizeof(mystruct) = 8;alignof(MyStruct) = 8

    其中char偏移爲0,int 的偏移爲4,中間填充3字節數據

  • 將aligns(8)添加到結構體內部變量,設置內部變量的對齊值
struct  MyStruct
     {
        alignas(8) char member;
        alignas(8) int a;
     }mystruct;

    sizeof(mystruct) = 16;alignof(MyStruct) = 8

此時能夠看出,結構體對齊值爲內部變量最大對齊值8,其中char變量對齊值爲8,偏移量0,int對齊值爲8,偏移量爲8,中間   填充7字節數據,結尾填充4字節數據。可見 alignas()對於修飾的變量有效

  • aligna(16)
 struct alignas(16) MyStruct
     {
        char member;  
        int a;  
     }mystruct;

     sizeof(mystruct) = 16;alignof(MyStruct) = 16

  sizeof(mystruct) = 16;alignof(MyStruct) = 16

    其中char偏移量0,int偏移量爲4,中間填充3字節數據,結尾填充8字節數據

  • #pragma pack(16)

     sizeof(mystruct) = 8;alignof(MyStruct) = 4

    能夠看出經過預編譯指令設置對齊失效了。

aligna(16)和#pragma(1)
    #pragma pack(1)
     struct alignas(8) MyStruct
     {
        char member;  
        int a;  
     }mystruct;

     sizeof(mystruct) = 8;alignof(MyStruct) = 8

    設置了預編譯指令和alignas(),遵循alignas()

4、總結

咱們能夠經過兩種方式來設置對齊,分別是#pragam pack()和alignas()/declspce(align(#)) 
對於#pragma pack()咱們能夠設置全局對齊方式,結構體和結構體內部變量都將遵循設置的對齊參數;而alignas()對修飾的單個變量是,對齊參數有效,例:alignas()修飾結構體變量,那麼結構體變量遵循該對齊參數,內部變量遵循默認的對齊參數,以上對齊方式均是對結構體變量起做用,對於數據區的其餘變量無論如何設置對齊方式,還會按照默認的字節來對齊。
#pragma pack()這種對齊參數設置有最大上限,最大設置爲其默認對齊參數;alignas()/declspce(align(#))這種對齊方式存在最小下限,最小下限爲默認對齊參數。對於設置兩種對齊方式,優先遵循alignas()這種對齊方式。結構體的默認對齊方式爲4byte,最終的結構體對齊方式與結構體內所佔最大空間的項的對齊方式一致結構體所佔空間必定爲對齊參數的整數倍,也就意味着結構內部可能會存在填充的字節

相關文章
相關標籤/搜索