我們都知道計算機是以位元組(byte)為單位劃分的,但是大部分處理器並不是按位元組塊來訪問記憶體的。它一般會以2位元組,4位元組,8位元組,16位元組甚至32位元組為單位來訪問記憶體。
32位系統下,int佔4byte,char佔乙個byte,那麼將它們放到乙個結構體中應該佔4+1=5byte?實際上,通過執行程式得到的結果是8 byte,這就是記憶體對齊所導致的。下面我們來介紹什麼是記憶體對齊?
對齊準則
1、char 1 位元組對齊 ,即存放 char 型的變數,記憶體單元的編號是 1 的倍數即可。在32位和64位的機器上,size_t的大小不同,如果在centos7上執行32位程式,我們需要安裝2、short int 2 位元組對齊 ,即存放 short int 型的變數,起始記憶體單元的編號是 2 的倍數即可。
3、int 4 位元組對齊 ,即存放 int 型的變數,起始記憶體單元的編號是 4 的倍數即可
4、long int 在 32 位平台下,4 位元組對齊 ,即存放 long int 型的變數,起始記憶體單元的編號是 4 的倍數即可 。
5、float 4 位元組對齊 ,即存放 float 型的變數,起始記憶體單元的編號是 4 的倍數即可
6、double
vc 環境下, 8 位元組對齊,即存放 double 型變數的起始位址,必須是 8 的倍數,double 變數佔 8 位元組
gcc 環境下, 4 位元組對齊,即存放 double 型變數的起始位址,必須是 4 的倍數,double 變數佔 8 位元組。
yum -y install gcc-multilib g+±multilib通過乙個小例子來說明是如何對齊的。
#include
struct
s1;/*
gcc -m32 test.c -o test
g++ -m32 test.cpp -o test
*/int
main
(int argc,
char
const
*ar**)
執行:
編譯成32位程式並執行,32位預設4位元組對齊,可以看到,結構體test 的大小為12位元組(a佔1位元組,b佔4位元組,c佔1位元組),而不是6位元組。
下面下圖:
上面的結構體,我們是不是可以考慮這樣寫:
struct
s1;
結構體變數大小是,它所有成員之和。 因為結構體變數是所有成員的集合。結構體變數分配記憶體的時候,是規則的。
給結構體變數分配記憶體的時候,會去結構體變數中找基本型別的成員哪個基本型別的成員佔位元組數多,就以它大小為單位開闢記憶體,
在 gcc **現了 double 型別例外
1、成員中只有 char 型資料 ,以 1 位元組為單位開闢記憶體。注意: 如果在結構體**現了陣列,陣列可以看成多個變數的集合。 如果出現指標的話,沒有佔位元組數更大的型別的,以 4 位元組為單位開闢記憶體。 在記憶體中儲存結構體成員的時候,按定義的結構體成員的順序儲存。2、成員**現了 short int 型別資料,沒有更大位元組數的基本型別資料。
以 2 位元組為單位開闢記憶體
3、出現了 int float 沒有更大位元組的基本型別資料的時候以 4 位元組為單位開闢記憶體。
4、出現了 double 型別的資料
情況 1:在 vc 裡,以 8 位元組為單位開闢記憶體。
情況 2:在 gcc 裡,以 4 位元組為單位開闢記憶體。 無論是那種環境,double 型變數,佔 8 位元組。
啊哈!這裡忘了說,使用union時,要留意平台的大小端問題。
大端模式,是指資料的高位元組儲存在記憶體的低位址中,而資料的低位元組儲存在記憶體的高位址中,這樣的儲存模式有點兒類似於把資料當作字串順序處理:位址由小向大增加,而資料從高位往低位放;這和我們的閱讀習慣一致。
小端模式,是指資料的高位元組儲存在記憶體的高位址中,而資料的低位元組儲存在記憶體的低位址中,這種儲存模式將位址的高低和資料位權有效地結合起來,高位址部分權值高,低位址部分權值低。怎麼獲知自己使用的平台的大小端?linux有個方法:
#include
static
union
endian_test =};
#define endianness ((char)endian_test.l)
intmain
(int argc,
char
const
*ar**)
編譯執行:
可以看出我的虛擬機器是小端。
為什麼要記憶體對齊?
從上面的結構體來看,本來只需要6位元組的空間,最後卻占用了12位元組,很明顯浪費了空間。為什麼要記憶體對齊,簡單的說記憶體對齊能夠提高 cpu 讀取資料的速度,減少 cpu 訪問資料的出錯性。
現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放。
#pragma pack的作用
#pragma pack 的主要作用就是改變編譯器的記憶體對齊方式。不同平台上編譯器的 pragma pack 預設值不同。而我們可以通過預編譯命令#pragma pack(n), n= 1,2,4,8,16來改變對齊係數。
#include
#pragma pack (1)
/*指定按1位元組對齊*/
struct d
;#pragma pack ()
/*取消指定對齊,恢復預設對齊*/
intmain
(int argc,
char
const
*ar**)
執行:
執行結果是7,上面的例子,應該對記憶體對齊有了全面的認識和了解,在以後的編碼中定義結構體時需要考慮成員變數定義的先後順序了。
總結
為了速度和正確性,請對齊你的資料。
1、結構體的記憶體對齊,按照其內部最大元素基本型別或者模數大小對齊。2、模數在不同平台值不一樣,也可通過#pragma pack(n)方式去改變。
3、結構體的記憶體大小,並非其內部元素大小之和。
結構體內存對齊
結構體內存對齊 一 什麼是位元組對齊,為什麼要對齊?現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這...
結構體內存對齊
一 什麼是位元組對齊,為什麼要對齊?現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊。對齊的...
結構體內存對齊
對齊規則 每個特定平台上的編譯器都有自己的預設 對齊係數 也叫對齊模數 程式設計師可以通過預編譯命令 pragma pack n n 1,2,4,8,16來改變這一係數,其中的n就是你要指定的 對齊係數 規則 1 資料成員對齊規則 結構 struct 的資料成員,第乙個資料成員放在offset為0的...