Posted on 2008-12-22 14:01
Shuffy 閱讀(5393)
評論(0) 編輯 收藏 引用 所屬分類:
VC++/C/C++/C#瀏覽集合
【大端(Big Endian)與小端(Little Endian)簡介】
Byte Endian是指字節(jié)在內(nèi)存中的組織,所以也稱它為Byte Ordering,或Byte Order。
對于數(shù)據(jù)中跨越多個字節(jié)的對象, 我們必須為它建立這樣的約定:
(1) 它的地址是多少?
(2) 它的字節(jié)在內(nèi)存中是如何組織的?
針對第一個問題,有這樣的解釋:
對于跨越多個字節(jié)的對象,一般它所占的字節(jié)都是連續(xù)的,它的地址等于它所占字節(jié)最低地址。(鏈表可能是個例外, 但鏈表的地址可看作鏈表頭的地址)。
比如: int x, 它的地址為0x100。 那么它占據(jù)了內(nèi)存中的Ox100, 0x101, 0x102, 0x103這四個字節(jié)(32位系統(tǒng),所以int占用4個字節(jié))。
上面只是內(nèi)存字節(jié)組織的一種情況: 多字節(jié)對象在內(nèi)存中的組織有一般有兩種約定。 考慮一個W位的整數(shù)。
它的各位表達如下:[Xw-1, Xw-2, ... , X1, X0],它的
MSB (Most Significant Byte, 最高有效字節(jié))為 [Xw-1, Xw-2, ... Xw-8];
LSB (Least Significant Byte, 最低有效字節(jié))為 [X7,X6,..., X0]。
其余的字節(jié)位于MSB, LSB之間。
LSB和MSB誰位于內(nèi)存的最低地址, 即誰代表該對象的地址?
這就引出了大端(Big Endian)與小端(Little Endian)的問題。
如果LSB在MSB前面, 既LSB是低地址, 則該機器是小端; 反之則是大端。
DEC (Digital Equipment Corporation,現(xiàn)在是Compaq公司的一部分)和Intel的機器(X86平臺)一般采用小端。
IBM, Motorola(Power PC), Sun的機器一般采用大端。
當然,這不代表所有情況。有的CPU即能工作于小端, 又能工作于大端, 比如ARM, Alpha,摩托羅拉的PowerPC。 具體情形參考處理器手冊。
具體這類CPU是大端還是小端,應該和具體設置有關。
(如,Power PC支持little-endian字節(jié)序,但在默認配置時是big-endian字節(jié)序)
一般來說,大部分用戶的操作系統(tǒng)(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。
所以說,Little Endian還是Big Endian與操作系統(tǒng)和芯片類型都有關系。
Linux系統(tǒng)中,你可以在/usr/include/中(包括子目錄)查找字符串BYTE_ORDER(或
_BYTE_ORDER, __BYTE_ORDER),確定其值。BYTE_ORDER中文稱為字節(jié)序。這個值一般在endian.h或machine/endian.h文件中可以找到,有時在feature.h中,不同的操作系統(tǒng)可能有所不同。
big endian是指低地址存放最高有效字節(jié)(MSB),而little endian則是低地址存放最低有效字節(jié)(LSB)。
用文字說明可能比較抽象,下面用圖像加以說明。比如數(shù)字0x12345678在兩種不同字節(jié)序CPU中的存儲順序如下所示:
Big Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
從上面兩圖可以看出,采用big endian方式存儲數(shù)據(jù)是符合我們?nèi)祟惖乃季S習慣的.
為什么要注意字節(jié)序的問題呢?你可能這么問。當然,如果你寫的程序只在單機環(huán)境下面運行,并且不和別人的程序打交道,那么你完全可以忽略字節(jié)序的存在。但是,如果你的程序要跟別人的程序產(chǎn)生交互呢?在這里我想說說兩種語言。C/C++語言編寫的程序里數(shù)據(jù)存儲順序是跟編譯平臺所在的CPU相關的,而J***A編寫的程序則唯一采用big endian方式來存儲數(shù)據(jù)。試想,如果你用C/C++語言在x86平臺下編寫的程序跟別人的J***A程序互通時會產(chǎn)生什么結果?就拿上面的0x12345678來說,你的程序傳遞給別人的一個數(shù)據(jù),將指向0x12345678的指針傳給了J***A程序,由于J***A采取big endian方式存儲數(shù)據(jù),很自然的它會將你的數(shù)據(jù)翻譯為0x78563412。什么?竟然變成另外一個數(shù)字了?是的,就是這種后果。因此,在你的C程序傳給J***A程序之前有必要進行字節(jié)序的轉換工作。
無獨有偶,所有網(wǎng)絡協(xié)議也都是采用big endian的方式來傳輸數(shù)據(jù)的。所以有時我們也會把big endian方式稱之為網(wǎng)絡字節(jié)序。當兩臺采用不同字節(jié)序的主機通信時,在發(fā)送數(shù)據(jù)之前都必須經(jīng)過字節(jié)序的轉換成為網(wǎng)絡字節(jié)序后再進行傳輸。ANSI C中提供了下面四個轉換字節(jié)序的宏。
·BE和LE一文的補完
我在8月9號的《Big Endian和Little Endian》一文中談了字節(jié)序的問題,原文見上面的超級鏈接??墒怯信笥讶匀粫?,CPU存儲一個字節(jié)的數(shù)據(jù)時其字節(jié)內(nèi)的8個比特之間的順序是否也有big endian和little endian之分?或者說是否有比特序的不同?
實際上,這個比特序是同樣存在的。下面以數(shù)字0xB4(10110100)用圖加以說明。
Big Endian
msb lsb
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
lsb msb
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
實際上,由于CPU存儲數(shù)據(jù)操作的最小單位是一個字節(jié),其內(nèi)部的比特序是什么樣對我們的程序來說是一個黑盒子。也就是說,你給我一個指向0xB4這個數(shù)的指針,對于big endian方式的CPU來說,它是從左往右依次讀取這個數(shù)的8個比特;而對于little endian方式的CPU來說,則正好相反,是從右往左依次讀取這個數(shù)的8個比特。而我們的程序通過這個指針訪問后得到的數(shù)就是0xB4,字節(jié)內(nèi)部的比特序?qū)τ诔绦騺碚f是不可見的,其實這點對于單機上的字節(jié)序來說也是一樣的。
那可能有人又會問,如果是網(wǎng)絡傳輸呢?會不會出問題?是不是也要通過什么函數(shù)轉換一下比特序?嗯,這個問題提得很好。假設little endian方式的CPU要傳給big endian方式CPU一個字節(jié)的話,其本身在傳輸之前會在本地就讀出這個8比特的數(shù),然后再按照網(wǎng)絡字節(jié)序的順序來傳輸這8個比特,這樣的話到了接收端不會出現(xiàn)任何問題。而假如要傳輸一個32比特的數(shù)的話,由于這個數(shù)在littel endian方存儲時占了4個字節(jié),而網(wǎng)絡傳輸是以字節(jié)為單位進行的,little endian方的CPU讀出第一個字節(jié)后發(fā)送,實際上這個字節(jié)是原數(shù)的LSB,到了接收方反倒成了MSB從而發(fā)生混亂。
【用函數(shù)判斷系統(tǒng)是Big Endian還是Little Endian】
bool IsBig_Endian()
//如果字節(jié)序為big-endian,返回true;
//反之為 little-endian,返回false
{
unsigned short test = 0x1122;
if(*( (unsigned char*) &test ) == 0x11)
return TRUE;
else
return FALSE;
}//IsBig_Endian()
以上資料整理自:
http://hi.baidu.com/serial_story/blog/item/7e110587c3ed8e29c75cc3c7.html
http://qzone.qq.com/blog/574754870-1219889620