C C 編譯器對struct大小的處理

2021-04-15 14:52:01 字數 3074 閱讀 9941

一、什麼是對齊,以及為什麼要對齊:

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

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

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

char

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

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

float

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

double

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

short

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

各 成員變數在存放的時候根據在結構中出現的順序依次申請空間,同時按照上面的對齊方式調整位置,空缺的位元組vc會自動填充。同時vc為了確保結構的大小為結 構的位元組邊界數(即該結構中占用最大空間的型別所占用的位元組數)的倍數,所以在為最後乙個成員變數申請空間後,還會根據需要自動填充空缺的位元組。

二、對齊的實現

通常,我們寫程式的時候,不需要考慮對齊問題。編譯器會替我們選擇適合目標平台的對齊策略。當然,我們也可以通知給編譯器傳遞預編譯指令而改變對指定資料的對齊方法。

但是,正因為我們一般不需要關心這個問題,所以因為編輯器對資料存放做了對齊,而我們不了解的話,常常會對一些問題感到迷惑。最常見的就是struct資料結構的sizeof結果,出乎意料。為此,我們需要對對齊演算法所了解。

對齊的演算法:

由於各個平台和編譯器的不同,現以本人使用的gcc version 3.2.2編譯器(32位x86平台)為例子,來討論編譯器對struct資料結構中的各成員如何進行對齊的。

struct mystruct

double dda1;

char dda;

int type

為 上面的結構分配空間的時候,vc根據成員變數出現的順序和對齊方式,先為第乙個成員dda1分配空間,其起始位址跟結構的起始位址相同(剛好偏移量0剛好 為sizeof(double)的倍數),該成員變數占用sizeof(double)=8個位元組;接下來為第二個成員dda分配空間,這時下乙個可以分 配的位址對於結構的起始位址的偏移量為8,是sizeof(char)的倍數,所以把dda存放在偏移量為8的地方滿足對齊方式,該成員變數占用 sizeof(char)=1個位元組;接下來為第三個成員type分配空間,這時下乙個可以分配的位址對於結構的起始位址的偏移量為9,不是sizeof (int)=4的倍數,為了滿足對齊方式對偏移量的約束問題,vc自動填充3個位元組(這三個位元組沒有放什麼東西),這時下乙個可以分配的位址對於結構的起 始位址的偏移量為12,剛好是sizeof(int)=4的倍數,所以把type存放在偏移量為12的地方,該成員變數占用sizeof(int)=4個 位元組;這時整個結構的成員變數已經都分配了空間,總的占用的空間大小為:8 1 3 4=16,剛好為結構的位元組邊界數(即結構中占用最大空間的型別所占用的位元組數sizeof(double)=8)的倍數,所以沒有空缺的位元組需要填充。 所以整個結構的大小為:sizeof(mystruct)=8 1 3 4=16,其中有3個位元組是vc自動填充的,沒有放任何有意義的東西。

再舉個例子好了,如:

例如,下面的結構各成員空間分配情況。

struct

test ;

結構的第乙個成員x1,其偏移位址為0,佔據了第1個位元組。第二個成員x2為short型別,其起始位址必須2位元組對界,因此,編譯器在x2和x1之間填充了乙個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然對界位址上,在它們前面不需要額外的填充位元組。在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大對界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。

位元組對齊的細節和編譯器實現相關,但一般而言,滿足三個準則:

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

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

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

注意的是:

基本型別是指前面提到的像char、short、int、float、double這樣的內建資料型別,這裡所說的」資料寬度」就是指其sizeof的大小。由於結構體的成員可以是復合型別,比如另外乙個結構體,所以在尋找最寬基本型別成員時,應當包括復合型別成員的子成員,而不是把復合成員看成是乙個整體。但在確定復合型別成員的偏移位置時則是將復合型別作為整體看待。

更改c編譯器的預設分配策略

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

使用偽指令#pragma pack ([n])

#pragma pack ([n])偽指令允許你選擇編譯器為資料分配空間所採取的對界策略。

例如,在使用了#pragma pack (1)偽指令後,test結構各成員的空間分配情況就是按照乙個位元組對齊了,格式如下:

#pragma pack(push) //儲存對齊狀態

#pragma pack(1)

//定義你的結構

//…………

#pragma pack(pop)

有 了以上的解釋,相信你對c語言的位元組對齊概念應該有了清楚的認識了吧。在網路程式中,掌握這個概念可是很重要的喔,在不同平台之間(比如在windows 和linux之間)傳遞2進製流(比如結構體),那麼在這兩個平台間必須要定義相同的對齊方式,不然莫名其妙的出了一些錯,可是很難排查的哦。

蒐集C C 編譯器

蒐集c c 編譯器在所有的和計算機的相關的專業中,c,c 幾乎都是必修課,而我們用的編譯器多半都是 turboc 2.0,visual c borland c builder 然而c c 的編譯器是何其多,長期使用乙個編譯器會讓我們形成思維的定視,就像當我們長期使用了 windows 再轉換成使用 ...

蒐集C C 編譯器

from 蒐集c c 編譯器 在所有的和計算機的相關的專業中,c,c 幾乎都是必修課,而我們用的編譯器多半都是turboc 2.0,visual c borland c builder,然而c c 的編譯器是何其多,長期使用乙個編譯器會讓我們形成思維的定視,就像當我們長期使用了windows再轉換成...

微軟C C 編譯器選項

微軟c c 編譯器選項 優化 o1 最小化空間 op 改善浮點數一致性 o2 最大化速度 os 優選 空間 oa 假設沒有別名 ot 優選 速度 ob內聯展開 預設 n 0 ow 假設交叉函式別名 od 禁用優化 預設值 ox 最大化選項。ogityb2 gs og 啟用全域性優化 oy 啟用框架指...