#字節(jié)對齊

[TOC]

什么是字節(jié)對齊(可以跳過)

現(xiàn)代計算機中內(nèi)存空間都是按照字節(jié)(byte)劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實際情況是在訪問特定變量的時候經(jīng)常在特定的內(nèi)存地址訪問,這就需要各類型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序地一個接一個地排放,這就是對齊.

字節(jié)對齊的好處(可以跳過)

為了提高效率,計算機從內(nèi)存中取數(shù)據(jù)是按照一個固定長度的。以32位機為例,它每次取32個位,也就是4個字節(jié)(每字節(jié)8個位)。字節(jié)對齊有什么好處?以int型數(shù)據(jù)為例,如果它在內(nèi)存中存放的位置按4字節(jié)對齊,也就是說1個int的數(shù)據(jù)全部落在計算機一次取數(shù)的區(qū)間內(nèi),那么只需要取一次就可以了.

如何對齊

通常,我們寫程序的時候,不需要考慮對齊問題,編譯器會替我們選擇適合目標平臺的對齊策略。但是,正因為我們一般不需要關(guān)心這個問題,所以,如果編輯器對數(shù)據(jù)存放做了對齊,而我們不了解的話,常常會對一些問題感到迷惑。最常見的就是struct數(shù)據(jù)結(jié)構(gòu)的sizeof結(jié)果,例如:

typedef struct
{
    char  member1;
    short member2;
    int   member3;
}Family;

//打印長度
NSLog(@"Family size is %zd",sizeof(Family));

//輸出結(jié)果
2016-07-22 15:34:29.081 Study[14587:5575156] Family size is 8

//下面我們更換一下成員變量位置,看看有什么效果

typedef struct
{
    char  member1;
    int   member3;
    short member2;
}Family;

//打印長度
NSLog(@"Family size is %zd",sizeof(Family));

//輸出結(jié)果

2016-07-22 15:36:25.126 Study[14591:5575689] Family size is 12

那么問題來了,兩個結(jié)構(gòu)體的成員變量只是改變了下順序,為什么占用的內(nèi)存大小不同呢?

對齊原則:

  • char 偏移量必須為sizeof(char) 即1的倍數(shù),可以任意地址開始存儲
  • short 偏移量必須為sizeof(short) 即2的倍數(shù),只能從0,2,4...等2的倍數(shù)的地址開始存儲
  • int 偏移量必須為sizeof(int) 即4的倍數(shù),只能從0,4,8...等4的倍數(shù)的地址開始存儲
  • float 偏移量必須為sizeof(float) 即4的倍數(shù),只能從0,4,8...等4的倍數(shù)的地址開始存儲
  • double 偏移量必須為sizeof(double)即8的倍數(shù),只能從0,8,16...等地址開始存儲

根據(jù)以上原則,我們來分析:

typedef struct
{
    char  member1;
    short member2;
    int   member3;
}Family;

  • 這個結(jié)構(gòu)體:member1占一個字節(jié),即 存儲位置為 0
  • member2占兩個字節(jié),根據(jù)上面原則,開始存儲地址應(yīng)該是2的倍數(shù),即 2~3
  • member3占4個字節(jié),根據(jù)原則,開始存儲地址是4的倍數(shù),即 4~7
  • 總共占用了0 ~ 7 共8個字節(jié).
    內(nèi)存存儲圖為:


    Paste_Image.png

下面分析:

typedef struct
{
    char  member1;
    int   member3;
    short member2;
}Family;

  • 這個結(jié)構(gòu)體:member1占一個字節(jié),即 0
  • member2占4個字節(jié),根據(jù)上面原則,開始存儲地址應(yīng)該是4的倍數(shù),即 4~7
  • member3占2個字節(jié),根據(jù)上面原則,開始存儲地址是2的倍數(shù),即 8 ~ 9
    總共占用了0 ~ 9 應(yīng)該是10個字節(jié),但為什么實際卻是12個字節(jié)呢 ?
    因為默認對齊方式是4字節(jié)(至于為什么,往下看),也就是說,總長度必須是4的倍數(shù),因此長度既要大于10,還要是4的倍數(shù),那就是12了.
    內(nèi)存結(jié)構(gòu)圖為:


    Paste_Image.png

結(jié)構(gòu)體如何設(shè)定字節(jié)對齊

  • 當未明確指定時,以結(jié)構(gòu)體中最長的成員的長度為其有效值,上面的兩個結(jié)構(gòu)體都是int類型最長,也就是4字節(jié)對齊

  • 結(jié)構(gòu)體作為成員:如果一個結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲.(struct a里存有struct b,b里有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲.)

  • 結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員的整數(shù)倍.不足的要補齊.

  • 當用#pragma pack(n)指定時,以n和結(jié)構(gòu)體中最長的成員的長度中較小者為其值.

  • 用#pragma pack()為還原字節(jié)對齊為默認值.

  • attribute ((packed)) 1字節(jié)對齊,此時結(jié)構(gòu)體的長度就是各成員變量長度之和.

例如:

pragma pack 的用法

struct A {
    int   a;
    char  b;
    short c;
};

struct B {
    char  b;
    int   a;
    short c;
};

#pragma pack (2) /*指定按2字節(jié)對齊*/
struct C {
    char  b;
    int   a;
    short c;
};
#pragma pack () /*取消指定對齊,恢復缺省對齊*/



#pragma pack (1) /*指定按1字節(jié)對齊*/
struct D {
    char  b;
    int   a;
    short c;
};
#pragma pack ()/*取消指定對齊,恢復缺省對齊*/

//計算所占字節(jié)
int s1=sizeof(struct A);
    int s2=sizeof(struct B);
    int s3=sizeof(struct C);
    int s4=sizeof(struct D);
    
    printf("%d\n",s1);
    printf("%d\n",s2);
    printf("%d\n",s3);
    printf("%d\n",s4);
//輸出結(jié)果:
8
12
8
7

attribute ((packed))的用法:
讓指定的結(jié)構(gòu)結(jié)構(gòu)體按照 1 字節(jié)對齊,例如:

//不加packed修飾
typedef struct {
    char    version;
    int16_t sid;
    int32_t len;
    int64_t time;
} Header;

//計算長度
NSLog(@"size is %zd",sizeof(Header));
輸出結(jié)果為:
2016-07-22 11:53:47.728 Study[14378:5523450] size is 16

可以看出,默認系統(tǒng)是按照4字節(jié)對齊

//加packed修飾
typedef struct {
    char    version;
    int16_t sid;
    int32_t len;
    int64_t time;
}__attribute__ ((packed)) Header;

//計算長度
NSLog(@"size is %zd",sizeof(Header));
輸出結(jié)果為:
2016-07-22 11:57:46.970 Study[14382:5524502] size is 15

用packed修飾后,變?yōu)?字節(jié)對齊,這個常用于與協(xié)議有關(guān)的網(wǎng)絡(luò)傳輸中.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • @[c++|struct] 今天在編程中碰到一個坑,搞的調(diào)試了半天,最后發(fā)現(xiàn)程序中在寫數(shù)據(jù)和讀取數(shù)據(jù)時結(jié)構(gòu)體定義不...
    drybeans閱讀 3,873評論 1 11
  • 4.3:新增class的相關(guān)內(nèi)容 今天看到一個題目: 最開始簡單的理解為,每個數(shù)據(jù)的size之和就是偏移量。因為偏...
    AwesomeAshe閱讀 992評論 0 0
  • 1. 對齊 需要各類型數(shù)據(jù)按照一定的規(guī)則在內(nèi)存空間上排列,而不是順序的排放,這就是對齊。 2. 對齊的原因 最常見...
    Sheldor936閱讀 411評論 0 0
  • (字節(jié)對齊的實現(xiàn)細節(jié)和編譯器有關(guān)) 1. 基本概念 字節(jié)對齊:計算機存儲系統(tǒng)中以Byte為單位存儲數(shù)據(jù),不同數(shù)據(jù)類...
    安然_fc00閱讀 2,986評論 0 1
  • 通過一段代碼來描述內(nèi)存對齊的現(xiàn)象。 上述代碼打印出來的結(jié)果為:24,16 為什么相同的結(jié)構(gòu)體,只是交換了變量 ab...
    豆瓣菜閱讀 7,043評論 5 26

友情鏈接更多精彩內(nèi)容