C 語言學習(12) ---- C語言結(jié)構(gòu)體對齊

字節(jié)對齊

現(xiàn)在計算機系統(tǒng)的內(nèi)存都是按照字節(jié)劃分的,理論上對任何變量的訪問可以從任何地址開始挖藏,但是實際情況是訪問特定的變量經(jīng)常在特定的內(nèi)存地址訪問,這就需要各種類型的數(shù)據(jù)按照一定的規(guī)則在空間上進行排列,而不是順序排放仍稀,這就是對齊的概念拳芙;
比如在32bit system 下察藐,一個4字節(jié)的int,如果它的地址是0x00000004 (4 的倍數(shù))舟扎,那么它就是對齊的分飞;如果是0x00000002(非4 的倍數(shù)),那么它就是非對齊的

  • 為什么要使用字節(jié)對齊浆竭?
    字節(jié)對齊的根本原因是在于CPU訪問效率的問題浸须,上面的例子中如果地址時0x00000002,那么CPU 需要訪問兩次內(nèi)存,
    第一次訪問0x000000002 ~ 0x00000003 得到一個2 byte 的內(nèi)容邦泄;
    第二次訪問0x000000004 ~ 0x00000005 在得到一個2 byte的內(nèi)容删窒,組合得到整形數(shù)據(jù)
    如果變量在對齊位置上,就可以一次取出數(shù)據(jù)顺囊,一些系統(tǒng)對對齊比較嚴格肌索,比如spar 系統(tǒng),取未對齊的數(shù)據(jù)就會發(fā)生錯誤特碳;在x86 上不會產(chǎn)生錯誤诚亚,只是效率下降;不同的對齊方式午乓,在內(nèi)存中存儲的方法可能不一樣站宗,合理利用字節(jié)對齊,可以有效節(jié)約存儲空間

結(jié)構(gòu)體對齊

結(jié)構(gòu)體對齊的規(guī)則首先要看有沒有用#pragma pack宏聲明益愈,使用這個預編譯指令可以改變對齊規(guī)則梢灭,
有宏定義的情況下結(jié)構(gòu)體自身的大小應為預編譯指定規(guī)定對齊的大小的整數(shù)倍,同時結(jié)構(gòu)體內(nèi)的成員的地址也會按照預編譯指定規(guī)定的大小對齊蒸其,#pragma pack 參數(shù)可以是 "1" "2" "4" "8" 或者 "16"

未使用pragma pack宏聲明

未使用pragma pack宏聲明的情況下敏释,遵循下面三個原則:

  1. 第一個成員的首地址假設為0
  2. 每個成員的首地址大小是其自身大小的整數(shù)倍
  3. 結(jié)構(gòu)體的總大小,是其成員中所包含的最大類型size大小的整數(shù)倍

示例一:

//  struct = 1+2+4+8
struct demostruct {
                 //  baseAddr     length  padding
    uint8_t a;  //  addr         1        1
    uint16_t b; //  addr+2       2        0
    uint32_t c; //  addr+4       4        0
    uint64_t d; //  addr+8       8        0
};
  • 上面的例子中如果沒有結(jié)構(gòu)體的內(nèi)存對齊摸袁,真實的大小為 1 + 2 + 4+ 8 = 15 字節(jié)
    按照結(jié)構(gòu)體里成員地址需要是其自身大小的整數(shù)倍的原則 假定為結(jié)構(gòu)體首地址為 addr
  1. 第一個成員 a 字節(jié)首地址 addr钥顽,長度為 1
  2. 第二個成員 b 字節(jié)首地址理論上為 addr + 1,但是按照地址對齊原則靠汁,地址必須是 2 的整數(shù)倍蜂大,所以在 a 后補充一個字節(jié),b的首地址變?yōu)?add + 2蝶怔,長度為 2
  3. 第三個成員 c 首地址理論上為 addr + 4奶浦,符合是自身長度整數(shù)倍的原則,長度為 4
  4. 第四個成員 d 首地址 addr + 8添谊,符合是自身長度整數(shù)倍的原則
  5. 結(jié)構(gòu)體大小為 16字節(jié)财喳,結(jié)構(gòu)體內(nèi)包含最大成員占用 8 字節(jié),符合上述原則

打印出的結(jié)構(gòu)體大小和各成員內(nèi)存地址如下:
struct_a size 16 a addr:008726E8 b:008726EA c addr:008726EC d addr:008726F0

對齊方法01.jpg

示例二:

//  struct = 1+8+1+1
struct demostruct_b {
    //  baseAddr     length  padding
    //  addr         1        7
    //  addr+8       8        0
    //  addr+16      1        0
    //  addr+17      1        0
    uint8_t a; 
    uint64_t b;
    uint8_t c;
    uint8_t d;
};
  • 上面的例子中如果沒有結(jié)構(gòu)體的內(nèi)存對齊,真實的大小為 1 + 1 + 8+ 1 = 11 字節(jié)
  1. 第一個成員 a 字節(jié)首地址 addr耳高,長度為 1
  2. 第二個成員 b 字節(jié)首地址理論上為 addr + 1扎瓶,但是按照地址對齊原則,地址必須是 8 的整數(shù)倍泌枪,所以在 a 后補充7個字節(jié)概荷,首地址變?yōu)?add + 8 長度為 8,
  3. 第三個成員 c 首地址為 addr + 16碌燕,長度為 1
  4. 第四個成員 d 首地址 addr + 17误证,長度為 1
  5. 結(jié)構(gòu)體大小為 18 字節(jié),結(jié)構(gòu)體內(nèi)包含最大成員占用 8 字節(jié)修壕,所以還需要在最后補充 6 字節(jié)愈捅,共 24 字節(jié)

打印出的結(jié)構(gòu)體大小和各成員內(nèi)存地址如下:
struct_b size 24 a addr:008726F8 b:00872700 c addr:00872708 addr:00872709

對齊方法02.jpg

使用#pragma pack宏聲明

#pragma pack(push)
#pragma pack(4)
//  struct = 1+8+1+1
struct demostruct_c {
    //  baseAddr     length  padding
    //  addr         1        3
    //  addr+4       8        0
    //  addr+12      1        0
    //  addr+13      1        0
    uint8_t a;
    uint64_t b;
    uint8_t c;
    uint8_t d;
};
#pragma pack(pop)
  • 上面的例子中用宏定義 按照 4 字節(jié)的方式對齊
  1. 第一個成員 a 字節(jié)首地址 addr,長度為 1
  2. 第二個成員 b 字節(jié)首地址理論上為 addr + 1慈鸠,但是按照地址對齊原則蓝谨,地址必須是 4 的整數(shù)倍,所以在 a 后補充3個字節(jié)青团,首地址變?yōu)?add + 4 長度為 8譬巫,
  3. 第三個成員 c 首地址為 addr + 12,長度為 1
  4. 第四個成員 d 首地址 addr + 13督笆,長度為 1
  5. 結(jié)構(gòu)體大小為14 字節(jié)芦昔,不是 pack(4) 的整數(shù)倍,所以在最后補充 2 字節(jié)娃肿,共 16 字節(jié)

struct_c size 16 a addr:00872710 b:00872714 c addr:0087271C addr:0087271D

pack對齊方法01.jpg

注意:
當#pragma pack 設定的 pack 值大于結(jié)構(gòu)體中最大成員的size 值時咕缎,應當以結(jié)構(gòu)體中最大成員的size作為 pack 的值
換句話說,應該以這兩者中較小的值作為 pack 的值

#pragma pack(push)
#pragma pack(8)
//  struct = 1+4 +2
struct demostruct_d {
    //  baseAddr     length  padding
    //  addr         1        3
    //  addr+4       4        0
    //  addr+8       2        0
    uint8_t a;
    uint32_t c;
    uint16_t b;
};
#pragma pack(pop)

驗證結(jié)果是12 字節(jié)咸作,不是以設定的 pack 值 8
*struct_d size 12 a addr:00EF2720 b:00EF2728 c addr:00EF2724

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锨阿,一起剝皮案震驚了整個濱河市宵睦,隨后出現(xiàn)的幾起案子记罚,更是在濱河造成了極大的恐慌,老刑警劉巖壳嚎,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桐智,死亡現(xiàn)場離奇詭異,居然都是意外死亡烟馅,警方通過查閱死者的電腦和手機说庭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來郑趁,“玉大人刊驴,你說我怎么就攤上這事。” “怎么了捆憎?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵舅柜,是天一觀的道長。 經(jīng)常有香客問我躲惰,道長致份,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任础拨,我火速辦了婚禮氮块,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诡宗。我一直安慰自己滔蝉,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布塔沃。 她就那樣靜靜地躺著锰提,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芳悲。 梳的紋絲不亂的頭發(fā)上立肘,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音名扛,去河邊找鬼谅年。 笑死,一個胖子當著我的面吹牛肮韧,可吹牛的內(nèi)容都是我干的融蹂。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼弄企,長吁一口氣:“原來是場噩夢啊……” “哼超燃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拘领,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤意乓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后约素,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體届良,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年圣猎,在試婚紗的時候發(fā)現(xiàn)自己被綠了士葫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡送悔,死狀恐怖慢显,靈堂內(nèi)的尸體忽然破棺而出爪模,到底是詐尸還是另有隱情,我是刑警寧澤荚藻,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布呻右,位于F島的核電站,受9級特大地震影響鞋喇,放射性物質(zhì)發(fā)生泄漏声滥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一侦香、第九天 我趴在偏房一處隱蔽的房頂上張望落塑。 院中可真熱鬧,春花似錦罐韩、人聲如沸憾赁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽龙考。三九已至,卻和暖如春矾睦,著一層夾襖步出監(jiān)牢的瞬間晦款,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工枚冗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缓溅,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓赁温,卻偏偏與公主長得像坛怪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子股囊,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容