c/c++ 位元組對齊

小石王發表於2018-06-15

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

相關文章