標籤:
結構體位移量
在定義結構體類型後,則結構體中的成員記憶體布局就定下了。
#include <stdio.h>typedef struct Test{ int a; int b; int c;}Test;int main(){ Test t; Test*p = NULL; p = &t; //b相對於結構體Test的位移量,n1=4 int n1 = (int)&(p->b) - (int)p; //絕對0地址b的位移量, n2=4 int n2 = (int)&( ( (Test *)0 )->b); printf("n1:%d \n", n1); printf("n2:%d \n", n2); return 0;}
記憶體位元組對齊
在用sizeof運算子求算某結構體所佔空間時,並不是簡單地將結構體中所有元素各自占的空間相加,這裡涉及到記憶體位元組對齊的問題。從理論上講,對於任何變數的訪問都可以從任何地址開始訪問,但是事實上不是如此,實際上訪問特定類型的變數只能在特定的地址訪問,這就需要各個變數在空間上按一定的規則排列, 而不是簡單地順序排列,這就是記憶體對齊。
測試代碼:
#include <stdio.h>typedef struct Test{ char a[5]; //5位元組 int b; //4位元組 int c; //4位元組}Test;int main(){ Test t; Test*p = NULL; p = &t; //b相對於結構體Test的位移量, n1的值為8,不是5 int n1 = (int)&(p->b) - (int)p; //絕對0地址b的位移量 int n2 = (int)&( ( (Test *)0 )->b); printf("n1:%d \n", n1); printf("n2:%d \n", n2); //sizeof(Test) = 8+4+4 = 16, 不是5+4+4 printf("sizeof(Test) = %d\n", sizeof(Test)); return 0;}
記憶體位元組對齊原因
- 某些平台只能在特定的地址處訪問特定類型的資料;
- 提高存取資料的速度。比如有的平台每次都是從偶地址處讀取資料,對於一個int型的變數,若從偶地址單元處存放,則只需一個讀取周期即可讀取該變數;但是若從奇地址單元處存放,則需要2個讀取周期讀取該變數。
記憶體位元組對齊原則
-
原則1:資料成員的對齊規則(預設以最大的類型位元組為單位)
結構體(struct)的資料成員,第一個資料成員放在offset為0的地方,以後每個資料成員存放在offset為該資料成員類型大小的整數倍的地方(比如int在32位機器為4位元組,則要從4的整數倍地址開始儲存)
-
原則2:結構體作為成員的對齊規則
如果一個結構體B裡嵌套另一個結構體A,還是以最大成員類型的位元組對齊,但是結構體A儲存起點為A內部最大成員整數倍的地方。(struct B裡存有struct A,A裡有char,int,double等成員,那A應該從8的整數倍開始儲存。),結構體A中的成員的對齊規則仍滿足原則1、原則2。
注意:
1)結構體A所佔的大小為該結構體成員內部最大元素的整數倍,不足補齊。
2)不是直接將結構體A的成員直接移動到結構體B中。
-
原則3:收尾工作
結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。
預設情況下,以結構體最大成員的類型位元組大小對齊,當然也可以通過程式控制,指定位元組對齊的單位:
#pragma pack(xx) //xx必須是2的n次方#pragma pack(1) //1位元組對齊#pragma pack(2) //2位元組對齊#pragma pack(4) //4位元組對齊#pragma pack(8) //8位元組對齊#pragma pack(16) //16位元組對齊
注意:儘管可以通過pragma pack()指定位元組對齊的單位,但是,只有當結構體成員大於或等於pragma pack()指定位元組對齊的單位,指定位元組對齊的單位才有效,否則,還是以結構體最大成員的類型位元組大小對齊
案例分析
結構體(struct)的資料成員,第一個資料成員放在位移量為0的地方,以後每個資料成員存放在位移量為該資料成員類型大小的整數倍的地方,但是,如果該資料成員類型大小大於位元組對齊大小,那麼位移量為位元組對齊大小的整數倍。
普通結構體案例1
struct{ char a; int b; short c;}A;
-
以1位元組對齊
當以1位元組對齊時,結構體的大小則為各成員空間大小相加:
sizeof(A) = 1 + 4 + 2 = 7
-
以2位元組對齊
a: 1 * 0 = 0
b: 2 * 1 = 2 (int為4位元組, 大於對齊位元組(2位元組),以2位元組計算)
c: 2 * 3 = 6
綜上:
a放在位移量為0的地方,由於a為char類型,大小為1位元組,所以,佔1個格子。
b放在位移量為2的地方,由於b為int類型,大小為4位元組,所以,佔4個格子,放2行,而上面的a,只佔一個位元組,不足對齊位元組(2位元組),故不足不齊(以”*”描述)。
c放在位移量為6的地方,由於c為short類型,大小為2位元組,所以,佔2個格子。
所以,sizeof(A) = 8
-
以4位元組對齊
a: 1 * 0 = 0
b: 4 * 1 = 4
c: 2 * 4 = 8
sizeof(A) = 12
-
以8或16位元組對齊
由於結構體成員最大的類型位元組為4,小於8或16,所以,儘管通過pragma pack(xx)指定對齊的位元組,但,還是以結構體成員最大的類型位元組對齊,還是以4位元組對齊。
案例2
struct{ char a[5]; double b; short c;}A;
-
以2位元組對齊
a: 1 * 0 = 0
b: 2 * 3 = 6 (double為8位元組, 大於對齊位元組(2位元組),以2位元組計算)
c: 2 * 7 = 14
sizeof(A) = 16
-
以4位元組對齊
a: 1 * 0 = 0
b: 4 * 2 = 8 (double為8位元組, 大於對齊位元組(4位元組),以4位元組計算)
c: 2 * 8 = 16
sizeof(A) = 20
-
以8或16位元組對齊
a: 1 * 0 = 0
b: 8 * 1 = 8
c: 2 * 8 = 16
sizeof(A) = 24
結構體嵌套結構體案例1
struct A{ char e; short f;};struct{ int a; char b; struct A c; char d;}B;
結構體B裡嵌套另一個結構體A,則以兩個結構體中最大的成員對齊,B中最大成員為int,4位元組,則以4位元組對齊。但是,A中的最大成員為short,2個位元組,A儲存起點位置必須為2的整數倍。
a: 4 * 0 = 0
b: 1 * 4 = 4
結構體起點位置:2*3=6, 為結構體A最大成員的整數倍
?e: 6 + 1*0 = 6
?f: 6 + 2*1 = 8
d: 1 * 10 = 10
sizeof(B) = 12
案例2
struct A{ char e; short f; int g;};typedef struct B{ int a; char b; struct A c; char d;}B;
結構體B裡嵌套另一個結構體A,則以兩個結構體中最大的成員對齊,B中最大成員為int,4位元組,則以4位元組對齊。但是,A中的最大成員為int,4個位元組,A儲存起點位置必須為4的整數倍。
a: 4 * 0 = 0
b: 1 * 4 = 4
結構體起點位置:2*4=8, 為結構體A最大成員的整數倍
?e: 8 + 1*0 = 8
?f: 8 + 2*1 = 10
?g: 8 + 4*1 = 12
d: 1 * 16 = 16
sizeof(B)=20
案例3
struct A{ char e; short f; double g;};typedef struct B{ int a; char b; struct A c; char d;}B;
結構體B裡嵌套另一個結構體A,則以兩個結構體中最大的成員對齊,最大成員為double,8位元組,則以8位元組對齊。但是,A中的最大成員為double,8個位元組,A儲存起點位置必須為8的整數倍。
a: 4 * 0 = 0
b: 1 * 4 = 4
結構體起點位置:8*1=8, 為結構體A最大成員的整數倍
?e: 8 + 1*0 = 8
?f: 8 + 2*1 = 10
?g: 8 + 8*1 = 16
d: 1 * 24 = 24
sizeof(B) = 32
位域結構體的位元組對齊
使用位域的主要目的是壓縮儲存,其大致規則為:
- 一個位域必須儲存在同一個位元組中,不能跨兩個位元組。如一個位元組所剩空間不夠存放另一位域時,應從下一單元起存放該位域。
- 如果相鄰位域欄位的類型相同,且其位寬之和小於類型的sizeof大小,則後面的欄位將緊鄰前一個欄位儲存,直到不能容納為止。
- 如果相鄰位域欄位的類型相同,但其位寬之和大於類型的sizeof大小,則後面的欄位將從新的儲存單元開始,其位移量為其類型大小的整數倍。
- 如果相鄰的位域欄位的類型不同,則各編譯器的具體實現有差異,vc6.0和vs採取不壓縮方式,Dev-C++和gcc採取壓縮方式。
- 如果位域欄位之間穿插著非位域欄位,則不進行壓縮。
struct A{ int a1 : 5; int a2 : 9; char b; int c : 4; short d;}B;
由於a1和a2是緊挨著,而且類型相同,故a1和a2壓縮放在一起,a1+a2=14位 < 32位,小於int類型的大小,故a1和a2隻需要1個int類型大小的空間,儘管沒有只佔14位,後面的不足補齊。
c儘管只佔4位,但由於前後的成員(b, d)類型都不是int,故c需要4位元組(32位)空間存放。
-
以1位元組對齊
a1+a2 = 14位,小於4位元組(int類型為4位元組),所以,a1和a2放在一起。
sizeof(B) = 4 + 1 + 4 + 2 = 11
-
以2位元組對齊
a1+a2: 2 * 0 = 0 (int為4位元組, 大於對齊位元組(2位元組),以2位元組計算)
b: 1 * 4 = 4
c: 2 * 3 = 6 (int為4位元組, 大於對齊位元組(2位元組),以2位元組計算)
d: 2 * 5 = 10
sizeof(B) = 12
-
以4、8或16位元組對齊
a1+a2: 4 * 0 = 0
b: 4 * 1 = 4
c: 4 * 2 = 8
d: 2 * 6 = 12
sizeof(B) = 16
參考資料:結構體位元組對齊
結構體位元組對齊