天天看點

結構體的大小

對齊:

   現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的通路可以從任何位址開始,但實際情況是在通路特定變量的時候經常在特定的記憶體位址通路,這就需要各類型資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。

對齊的作用和原因:

   各個硬體平台對存儲空間的處理上有很大的不同。一些平台對某些特定類型的資料隻能從某些特定位址開始存取。其他平台可能沒有這種情況, 但是最常見的是如果不按照适合其平台的要求對資料存放進行對齊,會在存取效率上帶來損失。比如有些平台每次讀都是從偶位址開始,如果一個int型(假設為 32位)如果存放在偶位址開始的地方,那麼一個讀周期就可以讀出,而如果存放在奇位址開始的地方,就可能會需要2個讀周期,并對兩次讀出的結果的高低 位元組進行拼湊才能得到該int資料。顯然在讀取效率上下降很多。這也是空間和時間的博弈。

對齊的實作

    通常,我們寫程式的時候,不需要考慮對齊問題。編譯器會替我們選擇适合目标平台的對齊政策。當然,我們也可以通知給編譯器傳遞預編譯指令而改變對指定資料的對齊方法。預設情況下,編譯器為結構體的每個成員按其自然對界(natural alignment)條件配置設定空間。各個成員按照它們被聲明的順序在記憶體中順序存儲,第一個成員的位址和整個結構的位址相同。自然對界(natural alignment)即預設對齊方式,是指按結構體的成員中size最大的成員對齊.

   最常見的就是struct資料結構的sizeof結果,出乎意料。為此,我們需要對對齊算法所了解。

   結構體的sizeof值,并不是簡單的将其中各元素所占位元組相加,而是要考慮到存儲空間的位元組對齊問題。先看下面定義的兩個結構體.

struct s1

{

int a;

double b;

int c;

};

struct s2

{

int a;

int c;

double b;

};

sizeof(s1) = ?, sizeof(s2) = ?

結果:sizeof(s1) = 24,sizeof(s2) = 16

可見,雖然兩個結構體所含的元素相同,但因為其中存放的元素類型順序不一樣,所占位元組也出現差異。這就是位元組對齊原因。

通過位元組對齊,有助于加快計算機的取數速度,否則就得多花指令周期。

結構體預設的位元組對齊一般滿足三個準則:

1) 結構體變量的首位址能夠被其最寬基本類型成員的大小所整除;

2) 結構體每個成員相對于結構體首位址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組(internal adding);

3) 結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組(trailing padding)。

通過這三個原則,就不難了解上面兩個struct的差異了.

對于struct S1, 為了使double變量滿足位元組對其準則(2), 即其存儲位置相對于結構體首位址的offset是自身大小(double占8個位元組)的整數倍,必須在位元組a後面填充4個位元組以對齊;再由準則(3),為了滿足結構體總大小為double大小的整數倍,必須再在c後面填充4個位元組,進而它的位元組數就是8*3=24。

對于struct S2, 卻不必如上所述的填充位元組,因為其直接順序存儲已經滿足了對齊準則。

指定對界

   一般地,可以通過下面的方法來改變預設的對界條件:

   使用僞指令#pragma pack (n),編譯器将按照n個位元組對齊;

   使用僞指令#pragma pack (),取消自定義位元組對齊方式。

   注意:如果#pragma pack (n)中指定的n大于結構體中最大成員的size,則其不起作用,結構體仍然按照size最大的成員進行對界。

結構體長度求法:

  a.成員都相同時(或含數組且數組資料類型同結構體其他成員資料類型):

  結構體長度=成員資料類型長度×成員個數(各成員長度之和);

  結構體中數組長度=數組資料類型長度×數組元素個數;

  b 成員不同且不含其它結構體時;

  (1).分析各個成員長度;

  (2).找出最大長度的成員長度M(結構體的長度一定是該成員的整數倍);

  (3).并按最大成員長度出現的位置将結構體分為若幹部分;

  (4).各個部分長度一次相加,求出大于該和的最小M的整數倍即為該部分長度

  (5).将各個部分長度相加之和即為結構體長度

  c含有其他結構體時:

  (1).分析各個成員長度;

  (2).對是結構體的成員,其長度按b來分析,且不會随着位置的變化而變化;

  (3).分析各個成員的長度(成員為結構體的分析其成員長度),求出最大值;

  (4).若長度最大成員在為結構體的成員中,則按結構體成員為分界點分界;

  其他成員中有最大長度的成員,則該成員為分界點;

  求出各段長度,求出大于該和的最小M的整數倍即為該部分長度

  (5).将各個部分長度相加之和即為結構體長度

  舉例來說:

  1.struct test1

  { int a;

   int b[4];

  };

  sizeof(test1)=sizeof(int)+4*sizeof(int)=4+4*4=20;

  2. struct test2

  { char a;

   int b;

   double c;

   bool d;

  };

  分析:該結構體最大長度double型,長度是8,是以結構體長度分兩部分:

  第一部分是a、 b、 c的長度和,長度分别為1,4,8,則該部分長度和為13,取8的大于13的最小倍數為16;

  第二部分為d,長度為1,取大于1的8的最小倍數為8,

  兩部分和為24,故sizeof(test2)=24;

  3. struct test3

  {

   char a;

   test2 bb;//見上題

   int cc;

  }

  分析:該結構體有三個成員,其中第二個bb是類型為test2的結構體,長度為24,且該結構體最大長度成員類型為double型,以後成員中沒有double型,是以按bb分界為兩部分:

  第一部分有a 、bb兩部分,a長度為1,bb長度為24,取8的大于25的最小倍數32;

  第二部分有cc,長度為4,去8的大于4的最小倍數為8;

  兩部分之和為40,故sizeof(test3)=40;

  4. struct test4

  {

   char a;

   int b;

  };

  struct test5

  { char c;

   test4 d;

   double e;

   bool f;

  };

  求sizeof(test5)

  分析:test5明顯含有結構體test4,按例2容易知道sizeof(test4)=8,且其成員最大長度為4;則結構體test5的最大成員長度為8(double 型),考試.大提示e是分界點,分test5為兩部分:

  第一部分由c 、d、e組成,長度為1、8、8,故和為17,取8的大于17的最小倍數為24;

  第二部分由f組成,長度為1,取8的大于1的最小倍數為8,

  兩部分和為32,故sizeof(test5)=24+8=32;

  在VC6.0下程式驗證 結果是正确的。

繼續閱讀