1. 程式人生 > >struct與union位元組大小的終極解釋

struct與union位元組大小的終極解釋

1、位元組對齊的細節和編譯器實現相關,但一般而言,如在windows下,就VC而言,滿足一下三個準則:
1) 結構體變數的首地址能夠被其最寬基本型別成員的大小所整除;
2) 結構體每個成員相對於結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組(internal adding);

即:在預設情況下,VC規定各成員變數存放的起始地址相對於結構的起始地址的偏移量:sizeof(型別)或其倍數
3) 結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組(trailing padding)

即:最大sizeof(型別)的整數倍



型別
對齊方式(變數存放的起始地址相對於結構的起始地址的偏移量)

Char   偏移量必須為sizeof(char)即1的倍數

int  偏移量必須為sizeof(int)即4的倍數

float  偏移量必須為sizeof(float)即4的倍數

double 偏移量必須為sizeof(double)即8的倍數
 

Short  偏移量必須為sizeof(short)即2的倍數

簡單說明:

struct DoNothing

{

};

這個sizeof(DoNothing)肯定是1的,原因是一個“沒有空間”的變數如何去取它的地址呢?如果有兩個不同的空struct如何去區分它們呢?這樣編譯器就只好這麼處理了。

struct MyStruct
{


char dda;       //偏移量為0,滿足對齊方式,dda佔用1個位元組;

double dda1;  //下一個可用的地址的偏移量為1,不是sizeof(double)=8的倍數,需要補足7個位元組才能使偏移量變為8(滿足對齊方式),因此VC自動填充7個位元組,dda1存放在偏移量為8的地址上,它佔用8個節。

int type;    //下一個可用的地址的偏移量為16,是sizeof(int)=4的倍數,滿足int的對齊方式,所以不需要VC自動填充,type存/放在偏移量為16的地址上,它佔用4個位元組。

};//所有成員變數都分配了空間,空間總的大小為1 7 8 4=20,不是結構的節邊界數(即結構中佔用最大空間的型別所佔用的位元組數sizeof 字串2(double)=8)的倍數,所以需要填充4個位元組,以滿足結構的大小為sizeof(double)=8的倍數。

所以該結構總的大小為:sizeof(MyStruc)為1 7 8 4 4=24。其中總的有7 4=11個位元組是VC自動填充的,沒有放任何有意義的東西。


2、如何修改編譯器的預設對齊值? 
1.在VC IDE中,可以這樣修改:[Project]|[Settings],c/c++選項卡Category的Code Generation選項的Struct Member Alignment中修改,預設是8位元組。 
2.在編碼時,可以這樣動態修改:#pragma pack .注意:是pragma而不是progma. 
1) n位元組對齊就是說變數存放的起始地址的偏移量有兩種情況:第一、如果n大於等於該變數所佔用的位元組數,那麼偏 移量必須滿足預設的對齊方式,第二、如果n小於該變數的型別所佔用的位元組數,那麼偏移量為n的倍數,不用滿足預設的對齊方式。
2) 結構的總大小也有個約束條 件,分下面兩種情況:如果n大於所有成員變數型別所佔用的位元組數,那麼結構的總大小必須為佔用空間最大的變數佔用的空間數的倍數,否則是n的倍數。

 

總結,struct的大小通過以下兩點即可得出:

1.資料成員對齊規則:
n位元組對齊就是說變數存放的起始地址的偏移量:min(sizeof(型別),對齊方式)或其倍數.
2.整體對齊規則:
結構的總大小也有個約束條件:min(最大的sizeof(型別),對齊方式)的倍數.


下面舉例說明其用法。

#pragma pack(push) //儲存對齊狀態
#pragma pack(4)//設定為4位元組對齊

struct test
{
char m1;
double m4;
int m3;
};

#pragma pack(pop)//恢復對齊狀態

以上結構的大小為16,下面分析其儲存情況,首先為m1分配空間,其偏移量為0,滿足我們自己設定的對齊方式(4位元組對齊),m1佔用1個位元組。接著開始為 m4分配空間,這時其偏移量為1,需要補足3個位元組,這樣使偏移量滿足為n=4的倍數(因為sizeof(double)大於n),m4佔用8個位元組。接 著為m3分配空間,這時其偏移量為12,滿足為4的倍數,m3佔用4個位元組。這時已經為所有成員變數分配了空間,共分配了16個位元組,滿足為n的倍數。如 果把上面的#pragma pack(4)改為#pragma pack(16),那麼我們可以得到結構的大小為24。

3、Union

union的長度取決於其中的長度最大的那個成員變數的長度。即union中成員變數是重疊擺放的,其開始地址相同。

其實union(共用體)的各個成員是以同一個地址開始存放的,每一個時刻只可以儲存一個成員,這樣就要求它在分配記憶體單元時候要滿足兩點:   
  1.一般而言,共用體型別實際佔用儲存空間為其最長的成員所佔的儲存空間;   
  2.若是該最長的儲存空間對其他成員的元型別(如果是陣列,取其型別的資料長度,例int   a[5]為4)不滿足整除關係,該最大空間自動延伸;   

  我們來看看這段程式碼:   

 union   mm{   
  char   a;//元長度1   
  int   b[5];//元長度4   
  double   c;//元長度8   
  int   d[3]; //元長度4
  };   

本來mm的空間應該是sizeof(int)*5=20;但是如果只是20個單元的話,那可以存幾個double型(8位)呢?兩個半?當然不可以,所以mm的空間延伸為既要大於20,又要滿足其他成員所需空間的整數倍,,因為含有double元長度8,故大小為24。
所以union的儲存空間先看它的成員中哪個佔的空間最大,拿他與其他成員的元長度比較,如果可以整除就行

轉自:https://www.cnblogs.com/cnjy/p/3820831.html

thx for:

http://www.cnblogs.com/alexkk2011/archive/2011/03/30/2000057.html

http://hi.baidu.com/gaomanyi/blog/item/9cf279638b96cb660d33fad0.html

http://blog.csdn.net/vincent_1011/article/details/4479965