c 位元組對齊
概念:
結構體裡會包括各種型別的成員,比如int char long等等,它們要佔用的空間不同,系統為一個結構體開闢記憶體空間時,會有2種選擇。
- 第一種:節省空間的方案,以上面的列子來說的話,就是4(int) + 1(char) + 8(long) =13個位元組;
- 第二種:浪費空間的方案,以上面的列子來說的話,就是4(int) + 4(char) + 8(long) =16個位元組;
其實,系統是用的第二種方案。
位元組對齊的目的:
為了CPU只尋找地址一次,就能夠把目標記憶體中的資料取出來。
現代計算機中記憶體空間都是按照byte劃分的 ,如果是用第一種節省空間的方案,為了要取一個int或者long的成員的值,CPU定址一次,可能只取出來一部分,所以需要再次定址,這樣就導致CPU的效率降低。為了提高CPU的效率,所以選擇犧牲空間,但是節省了時間。
經驗總結
宣告結構體成員的時候,一定把佔用空間最小的型別放在最前面,佔用空間最大的放在最後面,這樣就會節省記憶體空間。
程式驗證
#include <stdio.h>
typedef struct A{
char a;
char b;
char c;
}A;
//#pragma pack(4) //如果把註釋開啟就是強制按4位元組對齊,所以就是16
typedef struct B{//24
int a;//佔用4位元組 + 4 //往下看,下面的8位元組,所以補4個位元組
long b;//佔用8位元組
char c;//佔用1位元組+ 7 // 4+4+8+1=9,但9不是8的倍數,所以在最後的char c處加7個位元組
}B;
typedef struct C{//16
char a;//1 + 3
int b;//4
long c;//8
}C;
struct D{//32
int a;//4 + 4 //往下看,下面的8位元組,所以補4個位元組
struct S1{//16
long b;//8
char c;//1 + 1 //往下看,下面的2位元組,所以補1個位元組
short d;//2 + 4 //8+2+2=12但不是8的倍數,所以在最後的short d處加4個位元組
} aa;
int e;//4 + 4 //8+16+4=28但不是8的倍數,所以在最後的int e處加4個位元組
}D;
struct E{//40
int a;//4 + 4
struct S2{//24
short d;//2 + 6 //往下看,下面的8位元組,所以補6個位元組
long b;//8
char c;//1 + 7 //8+8+1=17但不是8的倍數,所以在最後的char c處加7個位元組
} aa;
int e;//4 + 4 ////8+24+4=36但不是8的倍數,所以在最後的int e處加4個位元組
}E;
struct F{//72
int a;//4 + 4
struct {//56
short d[20];//40 //不管陣列裡多少個元素,只看陣列的型別
long b;//8
char c;//1 + 7
} ;
int e;//4 + 4
}F;
int main(){
printf("A size = %ld
",sizeof(A));
printf("B size = %ld
",sizeof(B));
printf("C size = %ld
",sizeof(C));
printf("D size = %ld
",sizeof(D));
printf("E size = %ld
",sizeof(E));
printf("F size = %ld
",sizeof(F));
B B1;
B1.a = 10;
B1.b = 12;
B1.c = `A`;
}
執行結果:
A size = 3
B size = 24
C size = 16
D size = 32
E size = 40
F size = 72
位域
變數可以按位元位進行定義,比如定義只佔用1個位元位的char c:1;
只佔用10個位元位的int i:10;
定義的位元位不可以超過型別自身所佔用的位元組數
int i:33;//編譯不過,因為int佔用4個位元組,32個位元位,所以最多定義到int i:32;
#include <stdio.h>
struct A{//char佔用1個位元組8個位元位,a b c各佔用1個位元位,總共3個位元位,小於8個位元位,所以這個結構體佔用1個位元組
char a:1;
char b:1;
char c:1;
}A;
struct B{//char佔用1個位元組8個位元位,a b c總共佔用8個位元位,正好是1個位元組,所以這個結構體佔用1個位元組
char a:2;
char b:2;
char c:4;
}B;
struct B1{//char佔用1個位元組8個位元位,a b c總共佔用9個位元位,超過1個位元組,所以這個結構體佔用2個位元組
char a:2;
char b:2;
char c:5;
}B1;
struct C{//int佔用4個位元組32個位元位,a b總共佔用32個位元位,正好4個位元組,所以這個結構體佔用4個位元組
int a:31;
char b:1;
}C;
struct C1{//int佔用4個位元組32個位元位,a b總共佔用33個位元位,超過4個位元組,所以這個結構體佔用8個位元組,因為在char b處需要補3個位元組
int a:31;
char b:2;
}C1;
struct C2{//即使結構體C2看起來只佔用3個位元位,但是成員a是int型別,所以這個結構體佔用4個位元組
int a:1;
char b:2;
}C2;
int main(){
printf("A:%ld
", sizeof(A));
printf("B:%ld
", sizeof(B));
printf("B1:%ld
", sizeof(B1));
printf("C:%ld
", sizeof(C));
//結構體C裡的成員a佔用32個位元位的後31個位元位;成員b佔用第1個位元位
struct C sc;
sc.a =10;
sc.b = 1;
}
GDB的除錯結果:
40 sc.a =10;
(gdb) p &sc//列印出sc.a的地址
$1 = (struct C *) 0x7fffffffdecc
(gdb) x/tw 0x7fffffffdecc//用二進位制方式,產看記憶體地址裡的值
0x7fffffffdecc: 00000000000000000000000000000000
(gdb) n
41 sc.b = 1;
(gdb) x/tw 0x7fffffffdecc//發現10被放到記憶體中了,1010就是十進位制的10
0x7fffffffdecc: 00000000000000000000000000001010
(gdb) n
42 }
(gdb) x/tw 0x7fffffffdecc//發現1被放到記憶體的第一個位元位上了
0x7fffffffdecc: 10000000000000000000000000001010
執行結果:
A:1
B:1
B1:2
C:4
C1:8
C2:4