1. 程式人生 > >怎麼計算struct結構所佔空間?

怎麼計算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位元組!

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

  1. #include <stdio.h>

  2. struct A

  3. {

  4. char a;

  5. double b;

  6. int c;

  7. char d;

  8. };

  9. int main(void)

  10. {

  11. printf("sizeof(char) = %d\n", sizeof(char));

  12. printf("sizeof(int) = %d\n", sizeof(int));

  13. printf("sizeof(double) = %d\n\n", sizeof(double));

  14. printf("sizeof(struct A) = %d\n", sizeof(struct A));

  15. return 0;

  16. }

執行截圖:

                            

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

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

       (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 。