1. 程式人生 > >結構體--位元組對齊

結構體--位元組對齊

一、位元組對齊目的及原因
1.目的:為提高程式的效能
2.原因:為訪問未對齊的記憶體,處理器需要做兩次記憶體訪問,而對於對齊的記憶體僅需要訪問一次。
二、位元組對齊要區分的四個概念
1.基本資料型別的自身對齊值:(以32位x86機器為例)
char型:1位元組
short型:2位元組
int型、float型:4位元組
double型:8位元組
2.程式指定對齊值:

#pragma pack(value)

即指定對齊值為value
3. 自定義型別的自身對齊值:結構體或類中自身對齊值中最大的值
4. 自定義型別的有效對齊值:自定義型別的自身對齊值和指定對齊值中較小的值

例子:

#pragma pack(4)         //指定對齊值為4
struct test
{
    char a;    //1 + 1
    short b;   //2        自身對齊值為2 
    char c;    //1 + 1
};

//故該結構體的有效對齊值為2

三、對齊策略
1. 結構體變數的首地址是其有效對齊值的整數倍
編譯器給結構體開闢空間時,首先計算結構體的有效對齊值,然後尋找是其有效對齊值整數倍的記憶體地址,作為結構體的首地址
2. 結構體的每個成員相對於結構體首地址的偏移量是成員的有效對齊值的整數倍。(必要時編譯器會在成員之間加上填充位元組)
編譯器為結構體開闢空間之前會檢查預開闢空間的首地址相對於結構體首地址的偏移是否是本成員的有效對齊值的整數倍,若是,則存放本成員,若不是,會在本成員和上一個成員之間填充一定的位元組,已達到整數倍的要求,即將預開闢空間的首地址向後移動幾個位元組。
3. 結構體的總體大小為結構體的有效對齊值的整數倍(必要時編譯器會在最末一個成員之後加上填充位元組)
例如:

#pragma pack(4)        
struct test
{
    char a;    
    short b;        
    char c;    
};
#pragma pack()

假設test的首地址從0x0000開始,a的自身對齊值為1,比指定對齊值4小,所以其有效對齊值為1,符合0x0000%1=0;b的自身對齊值為2,比指定對齊值4小,所以其有效對齊值為2,因為0x0001%2!=0,所以編譯器會在a後面新增一個位元組,即0x0000–0x0001兩位元組中存放變數a,而0x0002–0x0003兩位元組中存放變數b,符合0x0002%2=0,同理c自身對齊值為1,有效對齊值也為1,故0x0004中存放變數c。現在結構體共有5位元組,而test自身對齊值為結構體變數中的自身對齊值的最大值,在這裡是變數b,即為2,小於指定對齊值4,故test的有效對齊值為2, 因為5%2!=0,所以編譯器會在最後一個變數c的末尾再新增一個位元組,即0x0000–0x0005共6個位元組的空間中存放結構體test.

四、位域
位域是指資訊在儲存時,並不需要佔用一個完整的位元組,而只佔用幾個或者一個二進位制位。所謂位域就是把一個位元組中的二進位制位劃分為幾個不同的區域,並說明每個區域的位數。每個域有一個域名,允許在程式中按域名進行操作。這樣就可以把幾個不同的物件用一個位元組的二進位制位來表示。
幾點說明:
1. 一個位域必須儲存在同一個位元組中,不能跨兩個位元組。一個位元組所剩空間不夠存放另一位域時,應從下一個單元起存放該位域。也可以有意使某位域從下一單元開始。

例如:

struct T
{
    unsigned a : 4;
    unsigned : 0;    //空域
    unsigned b : 4;   //從下一單元開始存放
    unsigned c : 4;
};
/*
a佔第一位元組的4位,後4位填0表示不使用,
b從第二位元組開始,佔用4位,c佔用4位
*/

位域的長度不能大於指定型別固有長度,如:int的位域長度不能超過32,bool型別的位域長度不能超過8
位域可以無位域名,這時它的作用是用來填充或調整位置的。無名的位域是不能使用的。
使用位域的目的是壓縮儲存,其規則如下:
1. 如果相鄰位域欄位型別相同,且其位寬之和小於型別的sizeof大小,則後面的欄位將緊鄰前一個欄位儲存,直到不能儲存為止;
2. 如果相鄰位域欄位型別相同,且其位寬之和大於型別的sizeof大小,則後面的欄位將從新的儲存單元開始,其偏移量為其型別大小的整數倍;
3. 如果相鄰的位域欄位之間穿插著非位域欄位,則不進行壓縮
4. 整個結構體的大小總為其有效對齊值的整數倍
5. 如果相鄰位域型別不同,依據編譯器的不同結果會不同
例如:

struct A{
    char s1 : 3;
    char s2 : 4;
    char s3 : 5;
};

第一個位元組僅能容納s1和s2,而s3則需要從第二個位元組開始儲存,一共兩個位元組,剛好是結構體的有效對齊值2的整數倍,故該結構體的大小為2。

struct B{
    char t1 : 3;
    short t2 : 4;
    char t3 : 5;
};

相鄰位域不同,在VS2013中sizeof(B)為6。

struct C{
    char r1 : 3;
    char r2;
    char r3 : 5;
};

有非位域欄位穿插在其中,不會產生壓縮,該結構體大小為3。

空結構體的大小為1