C語言再學習以內存對齊

昨天看Q3的代碼,看到有個_INTSAIZEOF的宏,着實暈了一陣。一番google後,終於明白,這個宏的做用是求出變量佔用內存空間的大小,先看看_INTSAIZEOF的定義吧:數據結構

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

(ANSI C標準下,_INTSAIZEOF宏定義在stdarg.h中,Q3中定義在bg_lib.h中;bg_lib.h -- standard C library replacement routines used by code)大數據

關於這個宏的內部原理,咱們後面再談,仍是先理理「內存對齊」這詞的意思吧,多年來一直模糊的存在於個人大腦中,究竟爲何要內存對齊啊。google

內存對齊的根源spa

一、許多計算機系統對基本數據類型可容許地址做了必定的限制,要求某種類型對象的地址必須是某個值n(一般是二、四、8)的倍數,從而來簡化處理器和存儲器之間的接口的硬件設計。如Linux的對齊策略是2字節數據類型,例如short的地址必須是2的倍數。而較大的數據類型如:int、int*、float、double則必須是4的倍數。而Microsoft Windows的策略要求更爲嚴格-----任何k字節對象的地址必須是k的倍數。好比要求一個double類型對象的地址必須是8的倍數(引自《深刻理解計算機系統》)。操作系統

二、數據結構(尤爲是棧)應該儘量地在天然邊界上對齊。緣由在於,爲了訪問未對齊的內存,處理器須要做兩次內存訪問;而對齊的內存訪問僅須要一次訪問。設計

 

內存對齊的規則code

上面所提到的天然邊界是由什麼決定的呢,我想應該是由硬件平臺決定的,至於操做系統和編譯器(默認對齊係數)則都是依賴於上一層的。固然,編譯器的對齊係數能夠經過預編譯命令#pragma pack(n),n=1,2,4,8,16來改變;對象

舉個例子:好比參數入棧,編譯器並非一個緊挨着一個的壓入棧的,而是根據對齊係數來壓棧的,好比一個char類型的參數,雖然自己只佔一個字節,可是編譯器會自動補全後面3個字節,而後再壓下一個參數。blog

 

(這裏說點題外話:在寫過delphi程序的人都知道,有個packed的保留字,做用就是壓縮數據結構,不要按對齊存儲,除非數據結構體積龐大,不然爲何要用packed呢,用了不就影響內存讀取的速度了嗎?:))接口

 

一、數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset爲0的地方,之後每一個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中,比較小的那個進行。 
二、結構(或聯合)的總體對齊規則:在數據成員完成各自對齊以後,結構(或聯合)自己也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行。 
三、結合一、2顆推斷:當#pragma pack的n值等於或超過全部數據成員長度的時候,這個n值的大小將不產生任何效果。

(以上3點引自《也談內存對齊》一文)

 

再看_INTSAIZEOF宏

這個宏的定義咋一看有點丈二和尚摸不着頭腦,不過網上有對齊的解釋,看後相信會豁然明朗了:

對於兩個正整數 x, n 總存在整數 q, r 使得 x = nq + r, 其中  0<= r <n                  //最小非負剩餘 q, r 是惟一肯定的。q = [x/n], r = x - n[x/n]. 這個是帶餘除法的一個簡單形式。在 c 語言中, q, r 容易計算出來: q = x/n, r = x % n. 所謂把 x 按 n 對齊指的是:若 r=0, 取 qn, 若 r>0, 取 (q+1)n. 這也至關於把 x 表示爲: x = nq + r', 其中 -n < r' <=0                //最大非正剩餘 nq 是咱們所求。關鍵是如何用 c 語言計算它。因爲咱們能處理標準的帶餘除法,因此能夠把這個式子轉換成一個標準的帶餘除法,而後加以處理: x+n = qn + (n+r'),其中 0<n+r'<=n            //最大正剩餘 x+n-1 = qn + (n+r'-1), 其中 0<= n+r'-1 <n    //最小非負剩餘 因此 qn = [(x+n-1)/n]n. 用 c 語言計算就是: ((x+n-1)/n)*n 若 n 是 2 的方冪, 好比 2^m,則除爲右移 m 位,乘爲左移 m 位。因此把 x+n-1 的最低 m 個二進制位清 0就能夠了。獲得: (x+n-1) & (~(n-1))

相關文章
相關標籤/搜索