计算机大端模式和小端模式 内存对齐问题(sizeof)
目錄(?)[+]
一、大端模式和小端模式的起源
? ? ? ? 關于大端小端名詞的由來,有一個有趣的故事,來自于Jonathan Swift的《格利佛游記》:Lilliput和Blefuscu這兩個強國在過去的36個月中一直在苦戰。戰爭的原因:大家都知道,吃雞蛋的時候,原始的方法是打破雞蛋較大的一端,可以那時的皇帝的祖父由于小時侯吃雞蛋,按這種方法把手指弄破了,因此他的父親,就下令,命令所有的子民吃雞蛋的時候,必須先打破雞蛋較小的一端,違令者重罰。然后老百姓對此法令極為反感,期間發生了多次叛亂,其中一個皇帝因此送命,另一個丟了王位,產生叛亂的原因就是另一個國家Blefuscu的國王大臣煽動起來的,叛亂平息后,就逃到這個帝國避難。據估計,先后幾次有11000余人情愿死也不肯去打破雞蛋較小的端吃雞蛋。這個其實諷刺當時英國和法國之間持續的沖突。Danny Cohen一位網絡協議的開創者,第一次使用這兩個術語指代字節順序,后來就被大家廣泛接受。?
二、什么是大端和小端
? ? ? ? Big-Endian和Little-Endian的定義如下:1) Little-Endian就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
2) Big-Endian就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。
舉一個例子,比如數字0x12 34 56 78在內存中的表示形式為:
1)大端模式:
低地址 -----------------> 高地址0x12 ?| ?0x34 ?| ?0x56 ?| ?0x78
2)小端模式:
低地址 ------------------> 高地址0x78 ?| ?0x56 ?| ?0x34 ?| ?0x12
可見,大端模式和字符串的存儲模式類似。
3)下面是兩個具體例子:
16bit寬的數0x1234在Little-endian模式(以及Big-endian模式)CPU內存中的存放方式(假設從地址0x4000開始存放)為:| 內存地址 | 小端模式存放內容 | 大端模式存放內容 |
| 0x4000 | 0x34 | 0x12 |
| 0x4001 | 0x12 | 0x34 |
32bit寬的數0x12345678在Little-endian模式以及Big-endian模式)CPU內存中的存放方式(假設從地址0x4000開始存放)為:
| 內存地址 | 小端模式存放內容 | 大端模式存放內容 |
| 0x4000 | 0x78 | 0x12 |
| 0x4001 | 0x56 | 0x34 |
| 0x4002 | 0x34 | 0x56 |
| 0x4003 | 0x12 | 0x78 |
?4)大端小端沒有誰優誰劣,各自優勢便是對方劣勢:
小端模式 :強制轉換數據不需要調整字節內容,1、2、4字節的存儲方式一樣。
大端模式 :符號位的判定固定為第一個字節,容易判斷正負。
三、數組在大端小端情況下的存儲:
以unsigned int value = 0x12345678為例,分別看看在兩種字節序下其存儲情況,我們可以用unsigned char buf[4]來表示value:Big-Endian: 低地址存放高位,如下:
高地址
? ? ? ? ---------------
? ? ? ? buf[3] (0x78) -- 低位
? ? ? ? buf[2] (0x56)
? ? ? ? buf[1] (0x34)
? ? ? ? buf[0] (0x12) -- 高位
? ? ? ? ---------------
? ? ? ? 低地址
Little-Endian: 低地址存放低位,如下:
高地址
? ? ? ? ---------------
? ? ? ? buf[3] (0x12) -- 高位
? ? ? ? buf[2] (0x34)
? ? ? ? buf[1] (0x56)
? ? ? ? buf[0] (0x78) -- 低位
? ? ? ? --------------
低地址
四、為什么會有大小端模式之分呢?
? ? ? 這是因為在計算機系統中,我們是以字節為單位的,每個地址單元都對應著一個字節,一個字節為8bit。但是在C語言中除了8bit的char之外,還有16bit的short型,32bit的long型(要看具體的編譯器),另外,對于位數大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個字節,那么必然存在著一個如果將多個字節安排的問題。因此就導致了大端存儲模式和小端存儲模式。例如一個16bit的short型x,在內存中的地址為0x0010,x的值為0x1122,那么0x11為高字節,0x22為低字節。對于大端模式,就將0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,剛好相反。我們常用的X86結構是小端模式,而KEIL C51則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端模式還是小端模式。
五、如何判斷機器的字節序
可以編寫一個小的測試程序來判斷機器的字節序:
[cpp] view plaincopy print?聯合體union的存放順序是所有成員都從低地址開始存放,利用該特性可以輕松地獲得了CPU對內存采用Little-endian還是Big-endian模式讀寫
[cpp] view plaincopy print?內存對齊問題
怎么判斷內存對齊規則,sizeof的結果怎么來的,請牢記以下3條原則:(在沒有#pragma pack宏的情況下,務必看完最后一行)
1:數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以后每個數據成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數組,結構體等)的整數倍開始(比如int在32位機為4字節,則要從4的整數倍地址開始存儲。
2:結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲.(struct a里存有struct b,b里有char,int ,double等元素,那b應該從8的整數倍開始存儲.)
3:收尾工作:結構體的總大小,也就是sizeof的結果,.必須是其內部最大成員的整數倍.不足的要補齊.
[cpp] view plaincopy print?結果是
48 24
ok,上面的全看明白了,內存對齊基本過關.
再講講#pragma pack().
在代碼前加一句#pragma pack(1),你會很高興的發現,上面的代碼輸出為
32 16
bb是4+8+4=16,aa是2+4+8+2+16=32;
這不是理想中的沒有內存對齊的世界嗎.沒錯,#pragmapack(1),告訴編譯器,所有的對齊都按照1的整數倍對齊,換句話說就是沒有對齊規則.
明白了不?
那#pragma pack(2)的結果又是多少呢?對不起,5分鐘到了,自己去測試吧.
ps:Vc,Vs等編譯器默認是#pragma pack(8),所以測試我們的規則會正常;注意gcc默認是#pragma pack(4),并且gcc只支持1,2,4對齊。套用三原則里計算的對齊值是不能大于#pragma pack指定的n值。
內存對齊二
VC對結構的存儲的特殊處理確實提高CPU存儲變量的速度,但是有時候也帶來了一些麻煩,我們也屏蔽掉變量默認的對齊方式,自己可以設定變量的對齊方式。VC 中提供了#pragma pack(n)來設定變量以n字節對齊方式。n字節對齊就是說變量存放的起始地址的偏移量有兩種情況:
第一、如果n大于等于該變量所占用的字節數,那么偏移量必須滿足默認的對齊方式;
第二、如果n小于該變量的類型所占用的字節數,那么偏移量為n的倍數,不用滿足默認的對齊方式。
結構的總大小也有個約束條件,分下面兩種情況:如果n大于所有成員變量類型所占用的字節數,那么結構的總大小必須為占用空間最大的變量占用的空間數的倍數;否則必須為n的倍數。下面舉例說明其用法:
[cpp] view plaincopy print?以上結構的大小為16,下面分析其存儲情況,首先為m1分配空間,其偏移量為0,滿足我們自己設定的對齊方式(4字節對齊),m1占用1個字節。接著開始為 m4分配空間,這時其偏移量為1,需要補足3個字節,這樣使偏移量滿足為n=4的倍數(因為sizeof(double)大于n),m4占用8個字節。接著為m3分配空間,這時其偏移量為12,滿足為4的倍數,m3占用4個字節。這時已經為所有成員變量分配了空間,共分配了4+8+4=16個字節,滿足為n的倍數。如果把上面的#pragma pack(4)改為#pragma pack(16),那么我們可以得到結構的大小為24。
再看下面這個例子:
[cpp] view plaincopy print?成員對齊有一個重要的條件,即每個成員分別對齊.即每個成員按自己的方式對齊.
也就是說上面雖然指定了按8字節對齊,但并不是所有的成員都是以8字節對齊.其對齊的規則是,每個成員按其類型的對齊參數(通常是這個類型的大小)和指定對齊參數(這里是8字節)中較小的一個對齊.并且結構的長度必須為所用過的所有對齊參數的整數倍,不夠就補空字節.
S1中,成員a是1字節默認按1字節對齊,指定對齊參數為8,這兩個值中取1,a按1字節對齊;成員b是4個字節,默認是按4字節對齊,這時就按4字節對齊,所以sizeof(S1)應該為8;
S2 中,c和S1中的a一樣,按1字節對齊,而d 是個結構,它是8個字節,它按什么對齊呢?對于結構來說,它的默認對齊方式就是它的所有成員使用的對齊參數中最大的一個,S1的就是4.所以,成員d就是 按4字節對齊.成員e是8個字節,它是默認按8字節對齊,和指定的一樣,所以它對到8字節的邊界上,這時,已經使用了12個字節了,所以又添加了4個字節的空,從第16個字節開始放置成員e.這時,長度為24,已經可以被8(成員e按8字節對齊)整除.這樣,sizeof(S2)為24個字節.
這里有三點很重要:
1.每個成員分別按自己的方式對齊,并能最小化長度。
2.復雜類型(如結構)的默認對齊方式是它最長的成員的對齊方式,這樣在成員是復雜類型時,可以最小化長度。
3.對齊后的長度必須是成員中最大的對齊參數的整數倍,這樣在處理數組時可以保證每一項都邊界對齊。
?
轉http://blog.csdn.NET/ce123_zhouwei/article/details/6971544
http://blog.csdn.Net/hairetz/article/details/4084088
http://www.cnblogs.com/cpoint/p/3369456.html
?未整理進來 http://blog.csdn.net/fanfank/article/details/12175585
總結
以上是生活随笔為你收集整理的计算机大端模式和小端模式 内存对齐问题(sizeof)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编程复杂性等级划分
- 下一篇: 静态方法和实例化方法的本质区别