程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 內存對齊

內存對齊

編輯:C++入門知識

結構體對齊也是老生常談的話題了。基本上是面試題的必考題。內容雖然很基礎,但一不小心就會弄錯。寫出一個struct,然後sizeof,你會不會經常對結果感到奇怪?sizeof的結果往往都比你聲明的變量總長度要大,這是怎麼回事呢?     開始學的時候,也被此類問題困擾很久。其實相關的文章很多,感覺說清楚的不多。結構體到底怎樣對齊?     有人給對齊原則做過總結,具體在哪裡看到現在已記不起來,這裡引用一下前人的經驗(在沒有#pragma pack宏的情況下):     原則1、數據成員對齊規則:結構(struct或聯合union)的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員存儲的起始位置要從該成員大小的整數倍開始(比如int在32位機為4字節,則要從4的整數倍地址開始存儲)。     原則2、結構體作為成員:如果一個結構裡有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲。(struct a裡存有struct b,b裡有char,int,double等元素,那b應該從8的整數倍開始存儲。)     原則3、收尾工作:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。     這三個原則具體怎樣理解呢?我們看下面幾個例子,通過實例來加深理解。     例1:struct {                  shorta1;                  shorta2;                  shorta3;                 }A;             struct{                 long a1;                 short a2;                 }B;          sizeof(A) = 6; 這個很好理解,三個short都為2。          sizeof(B) = 8; 這個比是不是比預想的大2個字節?long為4,short為2,整個為8,因為原則3。     例2:struct A{                   inta;                   charb;                   shortc;                   };            struct B{                   charb;                   inta;                   shortc;                   };        sizeof(A)= 8; int為4,char為1,short為2,這裡用到了原則1和原則3。       sizeof(B) = 12; 是否超出預想范圍?char為1,int為4,short為2,怎麼會是12?還是原則1和原則3。    深究一下,為什麼是這樣,我們可以看看內存裡的布局情況。                  a        b         c    A的內存布局:1111,    1*,       11                  b         a        c    B的內存布局:1***,     1111,   11**    其中星號*表示填充的字節。A中,b後面為何要補充一個字節?因為c為short,其起始位置要為2的倍數,就是原則1。c的後面沒有補充,因為b和c正好占用4個字節,整個A占用空間為4的倍數,也就是最大成員int類型的倍數,所以不用補充。    B中,b是char為1,b後面補充了3個字節,因為a是int為4,根據原則1,起始位置要為4的倍數,所以b後面要補充3個字節。c後面補充兩個字節,根據原則3,整個B占用空間要為4的倍數,c後面不補充,整個B的空間為10,不符,所以要補充2個字節。    再看一個結構中含有結構成員的例子:        例3:struct A{                     int a;                     double b;                     float c;                    };                struct B{                     char e[2];                     int f;                     double g;                     short h;                     struct A i;                    };       sizeof(A) = 24; 這個比較好理解,int為4,double為8,float為4,總長為8的倍數,補齊,所以整個A為24。       sizeof(B) = 48; 看看B的內存布局。                                 e         f            g               h                                   i      B的內存布局:11* *,   1111,   11111111, 11 * * * * **,        1111* * * *, 11111111, 1111 * * **     i其實就是A的內存布局。i的起始位置要為24的倍數,所以h後面要補齊。把B的內存布局弄清楚,有關結構體的對齊方式基本就算掌握了。     以上講的都是沒有#pragma pack宏的情況,如果有#pragma pack宏,對齊方式按照宏的定義來。比如上面的結構體前加#pragmapack(1),內存的布局就會完全改變。sizeof(A)= 16; sizeof(B) = 32;     有了#pragma pack(1),內存不會再遵循原則1和原則3了,按1字節對齊。沒錯,這不是理想中的沒有內存對齊的世界嗎。                                  a               b             c        A的內存布局:1111,     11111111,   1111                                 e        f            g         h                    i        B的內存布局:11,   1111,   11111111, 11,            1111,11111111, 1111        那#pragma pack(2)的結果又是多少呢?#pragma pack(4)呢?留給大家自己思考吧,相信沒有問題。        還有一種常見的情況,結構體中含位域字段。位域成員不能單獨被取sizeof值。C99規定int、unsigned int和bool可以作為位域類型,但編譯器幾乎都對此作了擴展,允許其它類型類型的存在。        使用位域的主要目的是壓縮存儲,其大致規則為:        1) 如果相鄰位域字段的類型相同,且其位寬之和小於類型的sizeof大小,則後面的字段將緊鄰前一個字段存儲,直到不能容納為止;        2) 如果相鄰位域字段的類型相同,但其位寬之和大於類型的sizeof大小,則後面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數倍;        3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實現有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式;        4) 如果位域字段之間穿插著非位域字段,則不進行壓縮;        5) 整個結構體的總大小為最寬基本類型成員大小的整數倍。         還是讓我們來看看例子。        例4:struct A{                      char f1 : 3;                     char f2 : 4;                     char f3 : 5;                     };                     a        b            c        A的內存布局:111,    1111 *,   11111 * * *        位域類型為char,第1個字節僅能容納下f1和f2,所以f2被壓縮到第1個字節中,而f3只能從下一個字節開始。因此sizeof(A)的結果為2。        例5:struct B{                    char f1 : 3;                    short f2 : 4;                    char f3 : 5;                    };        由於相鄰位域類型不同,在VC6中其sizeof為6,在Dev-C++中為2。        例6:struct C{                     char f1 : 3;                     char f2;                    char f3 : 5;                    };        非位域字段穿插在其中,不會產生壓縮,在VC6和Dev-C++中得到的大小均為3。        考慮一個問題,為什麼要設計內存對齊的處理方式呢?如果體系結構是不對齊的,成員將會一個挨一個存儲,顯然對齊更浪費了空間。那麼為什麼要使用對齊呢?體系結構的對齊和不對齊,是在時間和空間上的一個權衡。對齊節省了時間。假設一個體系結構的字長為w,那麼它同時就假設了在這種體系結構上對寬度為w的數據的處理最頻繁也是最重要的。它的設計也是從優先提高對w位數據操作的效率來考慮的。有興趣的可以google一下,人家就可以跟你解釋的,一大堆的道理。        最後順便提一點,在設計結構體的時候,一般會尊照一個習慣,就是把占用空間小的類型排在前面,占用空間大的類型排在後面,這樣可以相對節約一些對齊空間。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved