結(jié)構(gòu)體的對齊 在sizeof計算一個結(jié)構(gòu)體的大小時,經(jīng)常得到的值比結(jié)構(gòu)體內(nèi)部成員所占內(nèi)存總和要大,這就是因為在結(jié)構(gòu)體內(nèi)部,成員在存儲時有對齊的規(guī)則。結(jié)構(gòu)體對齊指的是 : 編譯器向結(jié)構(gòu)體插入無用內(nèi)存的能力,插入無用內(nèi)存使得結(jié)構(gòu)體成員以最佳方式對齊,從而得到更高的效能。當(dāng)基本數(shù)據(jù)類型以字節(jié)地址(幾倍于自身大?。┐鎯r,很多處理器能夠獲得最佳效能。 以下是幾個例子: Struct x { }; Struct y { }; Struct z { }; Sizeof(x)=12; X的內(nèi)存布局: Y的內(nèi)存布局: Z的內(nèi)存布局: 其中*表示填充的字節(jié),x中s后面為什么 要填充兩個 字節(jié)?因為i是整型,其起始位置要為4的倍數(shù)。C后面要填充3個字節(jié),因為結(jié)構(gòu)體size要為4(即最大類型 ——整型sizeof(int))的倍數(shù)。 Y中C后面填充一個 字節(jié),因為s為short類型,起始位置要為2的倍數(shù)。S后面沒有填充,因為c和s正好占用 了4個字節(jié)。 Z中s后面沒有填充,因為s和c正好占用4個字節(jié),c填充一個字節(jié) 因為struct大小要為int的整數(shù)倍。 再看一個有結(jié)構(gòu)體作為成員的例子: Struct A { }; Struct B { }; Sizeof(A)=24; 因為 int為4,double為8,float為4,總長為8的 倍數(shù),補(bǔ)齊,所以為24。 Sizeof(B)=48;看一下B的布局 B的內(nèi)存布局:e I其實就是A的內(nèi)存布局。I的 起始位置要為24的倍數(shù),所以h后面要補(bǔ)齊(A的最大類型是double,占8個字節(jié),所以i開始要8字節(jié)對齊,即8的倍數(shù),所以h要填充)。 通過把最大的數(shù)據(jù)類型放在結(jié)構(gòu)體的開始,最小數(shù)據(jù)類型放在結(jié)構(gòu)體的最后,這樣可以得到最小的結(jié)構(gòu)體size。 通過上面的例子可以總結(jié)一下三個規(guī)律: 1 數(shù)據(jù)成員對齊,結(jié)構(gòu)體(或聯(lián)合體)的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小的整數(shù)倍開始(比如int在32位機(jī)為4字節(jié),則要從4的整數(shù)倍地址開始存儲)。 2 結(jié)構(gòu)體作為成員,如果 一個 結(jié)構(gòu)體里面有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(struct b里面有struct a,a里面有char,int,double等元素,那么a應(yīng)該從8的整數(shù)倍開始存儲)。 3 結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補(bǔ)齊。 還有一種常見情況,結(jié)構(gòu)體中含有位域字段。位域成員不能單獨被取sizeof值。規(guī)定int,unsigned int和 bool可以作為位域類型,但編譯器幾乎都對此作了擴(kuò)展,允許其他類型的存在。使用位于的主要目的是壓縮存儲,其大致規(guī)則: 1 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的 字段將緊鄰前一個字段存儲,知道不能容納為止; 2 如果相鄰字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍; 3 如果相鄰的位域字段的類型 不同,則各編譯器的 具體實現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式; 4 如果位域字段之間穿插著非位域字段,則不進(jìn)行壓縮。 5 整個結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。 看下面例子: }; A的布局為 Struct B{ };相鄰位域的類型不同,在VC6中sizeof為6,在Dev-C++中為2. Struct C{ };非位域字段插在其中,不會產(chǎn)生壓縮,大小為3。 |
|