2012-10-10 21 views
2

可能重複:
When does Endianness become a factor?當字節序事做 - 轉換操作

閱讀本tuto上字節序,我落在這個例子字節順序很重要。這是關於寫一個字符*填充1和0.然後可以轉換爲一個短,結果取決於endianess,小或大。引用了這個例子。

unsigned char endian [2] = {1,0}; short x;

x = *(short *) endian; 

x的值是多少?讓我們看看這段代碼在做什麼。 您正在創建一個由兩個字節組成的數組,然後將兩個字節的數組 轉換爲一個短整型。通過使用一個數組,你基本上強制了 的某個字節順序,並且你將會看到系統如何處理這兩個字節。如果這是一個小端系統,則向後解釋0和1,並且看起來好像它是0,1。由於高字節爲 0,所以無關緊要,低字節爲1,因此x等於1.另一方面,如果是大端系統,則高字節爲1,並且值爲 x的是256

我想知道:當你實例化一個數組的內存給定的字節數分配(在這裏,兩個字節),怎麼能轉換做任何類型的(短型,整型... )只要數組已經分配了與這個字節對應的字節數?如果沒有足夠的內存分配給'包含此類型',下一個內存地址是否仍會被讀取?例如,如果我想將endian轉換爲long,將會執行這個操作,從endian的開頭讀取四個字節,還是會失敗?

然後,一個關於endianess的問題:這是處理器關於在內存中寫入字節的特性,在最低內存位置(大端)或最高內存位置(小端)最有意義的字節。在這種情況下,具有兩個單字節元素的數組已被分配。爲什麼說1是最有意義的字節?

+0

比這樣的演員陣容更多的是排在前面。鑄造目標的基本數據類型很可能也需要內存對齊前提條件。即這是一個非常微妙的方式來分段錯誤,並且只有一些時間。在下雨的星期六總是很有趣地跟蹤這些人。 – WhozCraig

回答

2

不要忘記,編譯器只會編寫彙編代碼。如果忽略編譯器的所有警告,則可以檢查編譯器生成的彙編代碼,並找出真正發生的事情。

我把這個簡單的程序:

#include <iostream> 

int main() 
{ 
    unsigned endian[2] = { 0, 0 } ; 
    long * casted_endian = reinterpret_cast<long*>(endian); 
    std::cout << *casted_endian << std::endl; 
} 

,我使用提取此objdump代碼。讓我們破譯它。

804879c: 55      push %ebp 
804879d: 89 e5     mov %esp,%ebp 
804879f: 83 e4 f0    and $0xfffffff0,%esp 
80487a2: 83 ec 20    sub $0x20,%esp 

這些行只是函數的序言,忽略它們。

unsigned endian[2] = { 0, 0 } ; 
80487a5: c7 44 24 14 00 00 00 movl $0x0,0x14(%esp) 
80487ac: 00 
80487ad: c7 44 24 18 00 00 00 movl $0x0,0x18(%esp) 
80487b4: 00 

從這些2行,可以看到(0×14)%尤指與初始化爲0。因此,你知道數組endian是在棧上,在地址在寄存器%ESP(堆棧指針) + 0x14。

long * casted_endian = reinterpret_cast<long*>(endian); 
80487b5: 8d 44 24 14    lea 0x14(%esp),%eax 

LEA只是一種算術運算。 EAX現在包含%ESP + 0x14,這是堆棧上陣列的地址。

80487b9: 89 44 24 1c    mov %eax,0x1c(%esp) 

,並在地址ESP +爲0x1C(這是變量casted_endian的位置),我們把EAX,所以尾數的第一個字節的地址。

std::cout << *casted_endian << std::endl; 
80487bd: 8b 44 24 1c    mov 0x1c(%esp),%eax 
80487c1: 8b 00     mov (%eax),%eax 
80487c3: 89 44 24 04    mov %eax,0x4(%esp) 
80487c7: c7 04 24 40 a0 04 08 movl $0x804a040,(%esp) 
80487ce: e8 1d fe ff ff   call 80485f0 <std::ostream::operator<<(long)@plt> 

然後,我們準備調用operator < <與相關參數沒有任何更多的檢查。所以就是這樣,程序不會再做任何檢查。變量的類型與機器完全無關。

operator<<將讀取不在數組中的部分*casted_endian時,現在可能發生兩件事情。

無論是它的地址是在當前存儲器映射頁面,或者它不是。在第一種情況下,operator<<將讀取該地址的任何內容而不抱怨。這可能會在屏幕上寫一些奇怪的東西。在第二種情況下,您的操作系統會抱怨程序嘗試讀取他無法讀取的內容,並引發中斷。這是着名的分段故障。

0

如果您嘗試轉換爲比數組大的大小,您將會收到未定義的行爲。它可能會嘗試讀取緊跟在數組後面的內存內容,但是結果不能保證,也不需要一致。

+0

使用Visual Studio 2010,您的行爲是正確的。 'long x = *(long *)endian;' '000000013FE0103B mov eax,dword ptr [endian]' '000000013FE0103F mov dword ptr [x],eax' –

0

哦,閣下。這裏我要說的是,爲什麼它可以在大多數體系結構上工作,但我不能說這有多少實際上是標準的。

你正在做什麼是鑄造陣列endian到一個簡短的。現在,數組基本上是指針,數組的名稱實際上包含第一個元素的地址。唯一真正的區別是數組包含更多有用的元數據,並且某些操作在陣列上不同(例如,sizeof)。然後您使用該地址(endian)並從中創建一個short指針。內存地址保持不變,只是您正在解釋指向不同的數據。然後,您取消引用該指針以取回該值,並將其分配給x

快速旁註。這可能不適用於所有系統。在C中,int僅被定義爲與架構的本地字大小一樣寬(x86上4個字節,x86_64上8個)。 short然後只被定義爲比int短(或者等於,如果內存正確地服務)。出於這個原因,該代碼將在8位體系結構上失敗。爲此,目標數據類型的字節大小必須等於或小於數組的大小。

同樣,long只定義爲長於int,通常分別爲x86和x86_64上的8或16個字節。在這種情況下,此代碼將在x86上工作:無論如何,處理器的字節序完全取決於處理器。 x86是小端(並且基本上啓動了LE設備的慣例,IIRC)。 SPARC是大端(直到9,可以是兩者)。 ARM和MIPS也是可配置的,Microblaze取決於使用的總線(AXI或PLB)。無論如何,字節序不僅限於處理器,與硬件或其他計算機通信時也是一個問題。

對於你最終的問題,最重要的字節被調用,因爲值是表示大於較小的字節可以表示的最大值。在16位字的情況下,最低有效字節可以表示0-255,最高有效字節256-65535。在任何情況下,除非你正在做低級別的系統編程(我的意思是直接修改內存)或編寫通信協議,否則你永遠不需要擔心需要擔心排序。

0
unsigned char endian[2] = {1, 0}; 
short x; 

x = *(short *) endian; 

此代碼具有未定義的行爲。結果可能是x設置爲1,256,4000,否則程序可能會崩潰或其他任何可能發生的事情。甚至在沒有考慮數組是否足夠大的情況下,情況就是如此。

下面的代碼的改寫,使其合法,做作者的意圖。

unsigned char endian[sizeof(short)] = {1}; 
short x; 
std::memcpy(&x, endian, sizeof(short)); 

如果你寫的是試圖得到一個int指出陣列的話,那就法律數組邊界之外的訪問代碼,你會再次打未定義行爲;什麼事情都可能發生。

在這種情況下,具有兩個單字節元素的數組已被分配。爲什麼說1是最有意義的字節?

(我猜你的意思是問,爲什麼endian[1]被稱爲持有最顯著字節)。

因爲在那個例子中,系統是小端,正如你說的,小的定義endian是地址最高的內存位置中最重要的字節。 endian[1]具有比endian[0]更高的地址,所以endian[1]將保存最高有效字節。

+0

thanks!如果我有'0x12345678',哪裏是最重要的字節?用'unsigned int temp = 0x12345678; char * tempAddress = &temp;',那麼當我是小端時,那麼'* tempAddress!= 0x12'是真的,是否正確(即0x12是最有意義的並且不在最低內存地址)? – octoback

+0

,以便使用此代碼,我在運行時檢查endianess,對不對?運行時你會做什麼樣的內核測試? – octoback

+0

@madptr'意義'是指特定職位可以影響價值多少。例如,三位十進制數字ABC A是最重要的,因爲它是數百個位置。數字B不太重要,因爲它只有十個位置。 C是最不重要的數字,因爲它是一個地方。 – bames53