怎麼計算struct結構所佔空間?

        C語言結構體struct所佔空間的計算一直是公司筆試題中的熱點。

        我們來看一下每種情況下的結構體的大小:

一、結構體成員是基本類型

        總體上遵循兩個原則:

       (1)、整體空間是佔用空間最大的成員(的類型)所佔字節數的整數倍。

       (2)、數據對齊原則---內存按結構體成員的先後順序排列,當排到該成員時,其前面已擺放的空間大小必須是該成員類型大小的整數倍,如果不夠則補齊,依次向後類推。
   
         某臺機器是32位,即這個機器上char 佔1個字節,int佔4個字節,double佔8字節

         我們舉個栗子:
         struct  A
         {
                 char  a;
                 double  b;
                 int  c;
                 char  d;
         };
        
         那麼,這個結構體佔用多少字節呢?

         <1>、它的第一個成員是char 類型的 a,佔 1 個字節。放入結構的0地址處:
         <2>、下來要存儲的是第二個成員是double 類型的 b,佔 8 個字節。它該怎麼存放呢?
                   我們回想到原則(2)中說,存儲到某個成員時,前面已經存放的所有成員所佔的總空間大小是改成員大小的整數倍,不夠則補齊!
                   也就是<1>圖中顯示的下一個存儲成員起始地址應該是 8 的整數倍,而上圖中顯示的起始地址爲 1 ,並不是 8 的整數倍,所以需要補齊。向後增加地址數到 8 (補齊操作),這時 8 是 8 的整數倍,可以存儲。則將成員 b 存放到 8 地址處:
          <3>、下來要存儲的是第三個成員是 int 類型的 c,佔 4 個字節,由上圖所知,下一個存儲成員的起始地址是16,剛好是 4 的倍數。所以不用補齊,直接用 4 個字節來存儲成員 c:
         <4>、最後存儲的是第四個成員是 char 類型的 d,佔 1 個字節。由上圖所知,下一個存儲成員的起始地址是20,剛好是 1 的倍數。所以不用補齊,直接用 1 個字節來存儲 成員 d:
       <5>、把所有成員都存儲到位,這就完了嗎?
                 No!我們還要考慮最後一個要素:原則(1),整體空間是佔用空間最大的成員(的類型)所佔字節數的整數倍!而這個結構的佔用空間最大的成員是 double 類型的 b,佔用 8 個字節。而現在整體空間由上圖所知是 21 個字節,並不是 8 的倍數,所以仍需要補齊!補多少呢? 比 21 大的最小的 8 的倍數是 24,所以就補到 24 字節:
           所以最終算下來,結構體 A 的大小是 24字節!

           最後,我們再來用實際代碼測試一下:

#include <stdio.h>

struct A
{
	char a;
	double b;
	int c;
	char d;
};

int main(void)
{
	printf("sizeof(char) = %d\n", sizeof(char));
	printf("sizeof(int) = %d\n", sizeof(int));
	printf("sizeof(double) = %d\n\n", sizeof(double));

    printf("sizeof(struct A) = %d\n", sizeof(struct A));
	return 0;
}

運行截圖:
                           



二、結構體成員是另外一個結構體時


        之前的兩個原則將做一些修改,我們把本結構體稱爲父結構體,把成員結構體變量稱爲子結構體:

       (1)、整體空間是子結構體與父結構體中佔用空間最大的成員(的類型)所佔字節數的整數倍。

       (2)、數據對齊原則---父結構體內存按結構體成員的先後順序排列,當排到子結構體成員時,其前面已擺放的空間大小必須是該子結構體成員中最大類型大小的整數倍,如果不夠則補齊,依次類推。
   
         某臺機器是32位,即這個機器上char 佔1個字節,int佔4個字節,double佔8字節
 
         結構體例子:
          struct  A
         {
                 char  a;
                 double  b;
                 int  c;
                 char  d;
         };

         struct  B
         {
                 char a;
                 struct  A  b;
                 int  c;
         };

         那麼,結構體 B 佔多少字節呢?通過之前分析,我們已知結構體 A 的大小爲 24 字節:

         <1>、首先存儲成員 a ,佔用 1 個字節。下一個存儲地址是 1

         <2>、其次存儲成員 b,而 b 是結構體A的變量,結構體A中最大類型大小double,佔8 個字節,所以存儲該變量地址應是 8 的倍數,所以需要補齊地址到 8 ,然後存儲大小爲 24 字節的變量 b:
         <3>、最後存儲成員 c,成員 c 類型是 int,佔用 4 個字節。由上圖可知 c 的存儲始地址是 32 ,剛好是 4 的倍數,並不用補齊,所以直接存儲:
         <4>、最後也要看整體空間大小是否滿足原則(1)
                   我們發現,當前結構體總大小是 36 ,而子結構體和父結構體中最大類型是double,佔 8 個字節。而 36 並非是 8 的整數倍,所以需要補齊爲 8 的整數倍。即  40 字節:
           所以最後,結構體B的大小是 40 字節。
     
           實例驗證:

        

三、結構體成員是數組類型

       之前的理解了,那麼這個也就不難理解!
       只要不把數組看作數組,而把它看成一個個類型相同的成員變量序列!然後用前面的計算方法計算即可。

四、包含位域成員的結構體大小計算
       
        使用位域的主要目的是壓縮存儲,其大致規則爲:
        (1)、如果相鄰位域字段的類型相同,且其位寬之和小於類型的sizeof大小,則後面的字段將緊鄰前一個字段存儲,直到不能容納爲止。
        (2)、如果相鄰位域字段的類型相同,但其位寬之和大於類型的sizeof,則後面的字段將從新的存儲單元開始,其偏移量爲其類型大小的整數倍。
        (3)、如果相鄰的位域字段的類型不同,則各編譯器的具體實現有差異。VC6採取不壓縮方式,gcc採取壓縮方式。
        (4)、如果位域字段之間穿插着非位域字段,則不進行壓縮。
        (5)、整個結構體的總大小爲最寬的基本類型成員大小的整數倍。
       
        舉個栗子:

        (1)在Linux + gcc 環境下,以下結構體的sizeof值是多少?
 
        struct  A
        {
               int  f1 : 3;
               char  b;
               char  c;
        }
 
        解析: sizeof( struct A ) 的值爲 4, 由於 f1 只佔用了 3 個位,不足一個字節,假設 f1 擺放在地址 0 處,則地址1可用,故 b 擺放在地址 1 處,則 c 擺放在 2 處, 0 ~ 2 共 3 個字節不是 int 的整數倍(整個結構體的總大小爲最寬基本類型成員int大小的整數倍),故地址 3 處補齊,共四個字節。如下圖:

            如果在VC環境下,sizeof(struct A)的值爲 8!

       (2)、
        
         struct  B
         {
                char  f1 : 3;
                char  f2 : 4;
                char  f3 : 5;
         }
 
         解析: 按照之前規則,第一個字節僅能容下f1 和 f2,所以 f2 被壓縮存儲到 第一個字節中,而f3 只能從下一個字節開始存儲,所以sizeof( struct B )的值爲 2 ;如下圖:
          (3)、相鄰位域類型不同的情況
           struct  C
           {
                  char  f1:3;
                  short  f2 : 4;
                  char f3 : 5;
           }
           解析: 由於相鄰位域類型不同,在VC中不壓縮,所以sizeof值爲 6 ,而在gcc中壓縮,其值爲 2 。

          (4)、
           
           struct  D
           {
                  char  f1:3;
                  short  f2 : 4;
                  char f3 : 5;
           }
           解析:非位域字段穿插其中,不會產生壓縮,在VC和gcc中sizeof大小均爲 3 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章