1. 程式人生 > >struct結構體佔記憶體大小計算

struct結構體佔記憶體大小計算

注意:struct 的{}後面要加上 ”;“

#include<stdio.h>
struct A
{
           int a;
          double b;
           char c;
};
struct B
{
           double b;
            char c;
            int a;
};
struct C
{
            int a;
            char c;
            double b;
};
int main(void)
{
          A aa;
          B bb;
          C cc;
          printf("A = %d\n", sizeof(aa));//結果:A = 24
          printf("B = %d\n", sizeof(bb));//結果:B = 16
          printf("C = %d\n", sizeof(cc));//結果:C = 16
          return 0;
}
int型別一般是佔用四個位元組,char型別一般佔用一個位元組,double型別一般佔用8個位元組。
1、結構體A首先給int a 分配四個位元組,並且以4個位元組對齊;然後給double b分配8個位元組,發現以4個位元組對齊不行,就以8個位元組對齊,前面只有int a ,所以int a將佔用8個位元組;最後為了對齊,將給char c 也分配8給位元組,所以結構體A佔用了24個位元組。
2、結構體B首先給double 分配8個位元組,並且以8給位元組對齊;然後給char c分配8給位元組;最後給int a分配空間的時候發現,前面空有7個位元組空間可以放下int a,int a 就和char c一起佔用8個位元組,所以結構體B佔用了16個位元組

而在GNU GCC編譯器中,遵循的準則有些區別,對齊模數不是像上面所述的那樣,根據最寬的基本資料型別來定。在GCC中,對齊模數的準則是:對齊模數最大隻能是4,也就是說,即使結構體中有double型別,對齊模數還是4,所以對齊模數只能是1,2,4。而且在上述的三條中,第2條裡,offset必須是成員大小的整數倍,如果這個成員大小小於等於4則按照上述準則進行,但是如果大於4了,則結構體每個成員相對於結構體首地址的偏移量(offset)只能按照是4的整數倍來進行判斷是否新增填充。
看如下例子:

struct T
{
  char ch;
  double   d   ;
};
那麼在GCC下,sizeof(T)應該等於12個位元組。

struct結構體的大小計算:
struct 大小,與pack的大小(在程式中顯示設定#pragma pack(),vc6.0預設大小為8)、結構中最大佔用有關

struct A
{

 int a;     0-3

               4-7     要填充(padding)以保證記憶體對齊的原則
 double b; 8-15
 char c[9]; 16-24
};

首先給a分配記憶體,因為int佔四個位元組<pack大小(8個位元組),所以按照4個位元組對齊,起始位值為0,0%4=0,a最後佔的記憶體為0-3;

接著給b分配記憶體,double佔8個位元組,所以按照8個位元組對齊,起始位為8(8%8=0),b為8-15;

最後char佔一個位元組,所以c為16-24;

因此結構體A的大小為8+8+9 = 25;有因為25不是結構體中最大佔用記憶體型別double型別大小(8)的整數倍,所以A最後的大小是32;

struct B

{
int a;   

double b;     
double c;    
char d;   

};

同理,按上面的分析方法確定各個變數在記憶體中的位置:
a,0-3;

  ,4-7;
b,8-15;
c,16-23;
d,24;

TOTAL = 25

25 不是8的倍數,所以最後B大小是32;

總結計算結構體的步驟

1.記憶體對齊與編譯器設定有關,首先要搞清編譯器這個預設值是多少

2.如果不想編譯器預設的話,可以通過#pragma pack(n)來指定按照n對齊

3.每個結構體變數對齊,如果對齊引數n,變數所佔位元組數(m),記憶體地址的起始位置%min(n,m)=0。也就是最小化長度規則

4.結構體總大小: 對齊後的長度必須是成員中最大的對齊引數的整數倍。

5.補充:如果結構體A中還要結構體B,那麼B的對齊方式是選它裡面最長的成員的對齊方式

所以計算結構體大小要走三步,首先確定是當前程式按照幾對齊(參照1,2點),接著計算每個結構體變數的大小和偏移(參照3,5),最後計算結構體總大小(參照4)。

如果結構體中含有位域(bit-field),那麼VC中準則又要有所更改:
1) 如果相鄰位域欄位的型別相同,且其位寬之和小於型別的sizeof大小,則後面的欄位將緊鄰前一個欄位儲存,直到不能容納為止;
2) 如果相鄰位域欄位的型別相同,但其位寬之和大於型別的sizeof大小,則後面的欄位將從新的儲存單元開始,其偏移量為其型別大小的整數倍;
3) 如果相鄰的位域欄位的型別不同,則各編譯器的具體實現有差異,VC6採取不壓縮方式(不同位域欄位存放在不同的位域型別位元組中),Dev-C++和GCC都採取壓縮方式;
備註:當兩欄位型別不一樣的時候,對於不壓縮方式,例如:


struct N
{
  char c:2;
  int    i:4;
};

依然要滿足不含位域結構體記憶體對齊準則第2條,i成員相對於結構體首地址的偏移應該是4的整數倍,所以c成員後要填充3個位元組,然後再開闢4個位元組的空間作為int型,其中4位用來存放i,所以上面結構體在VC中所佔空間為8個位元組;而對於採用壓縮方式的編譯器來說,遵循不含位域結構體記憶體對齊準則第2條,不同的是,如果填充的3個位元組能容納後面成員的位,則壓縮到填充位元組中,不能容納,則要單獨開闢空間,所以上面結構體N在GCC或者Dev-C++中所佔空間應該是4個位元組。

4) 如果位域欄位之間穿插著非位域欄位,則不進行壓縮;
備註:
結構體


typedef struct
{
   char c:2;
   double i;
   int c2:4;
}N3;

在GCC下佔據的空間為16位元組,在VC下佔據的空間應該是24個位元組。
5) 整個結構體的總大小為最寬基本型別成員大小的整數倍。

ps:


對齊模數的選擇只能是根據基本資料型別,所以對於結構體中巢狀結構體,只能考慮其拆分的基本資料型別。而對於對齊準則中的第2條,確是要將整個結構體看成是一個成員,成員大小按照該結構體根據對齊準則判斷所得的大小。 
類物件在記憶體中存放的方式和結構體類似,這裡就不再說明。需要指出的是,類物件的大小隻是包括類中非靜態成員變數所佔的空間,如果有虛擬函式,那麼再另外增加一個指標所佔的空間即可。