EI_DATA相關知識

通過使用readelf命令讀取ELF文件頭信息,可以看到Data字段(對應於ELF文件的EI_DATA)顯示信息爲:

2's complement, little endian,也就是2的補碼和小端編碼。

不熟悉這兩個概念的同學可以參考下面的兩篇文章。





小端格式和大端格式(Little-Endian&Big-Endian)

不同的CPU有不同的字節序類型,這些字節序是指整數在內存中保存的順序。
最常見的有兩種:
1. Little-endian:將低序字節存儲在起始地址(低位編址)
2. Big-endian:將高序字節存儲在起始地址(高位編址)

 
LE(little-endian):
最符合人的思維的字節序 
地址低位存儲值的低位 
地址高位存儲值的高位 
怎麼講是最符合人的思維的字節序,是因爲從人的第一觀感來說 
低位值小,就應該放在內存地址小的地方,也即內存地址低位 
反之,高位值就應該放在內存地址大的地方,也即內存地址高位 
 
BE(big-endian): 
最直觀的字節序 
地址低位存儲值的高位 
地址高位存儲值的低位 
爲什麼說直觀,不要考慮對應關係 
只需要把內存地址從左到右按照由低到高的順序寫出 
把值按照通常的高位到低位的順序寫出 
兩者對照,一個字節一個字節的填充進去 
 
例子1:在內存中雙字0x01020304(DWORD)的存儲方式。 
內存地址 4000 4001 4002 4003 
   LE      04   03   02   01 
   BE      01   02   03   04 
注:每個地址存1個字節,每個字有4個字節。2位16進制數是1個字節(0xFF=11111111)。

 
例子2:如果我們將0x1234abcd寫入到以0x0000開始的內存中,則結果爲
    big-endian   little-endian
0x0000    0x12         0xcd
0x0001    0x23         0xab
0x0002    0xab         0x34
0x0003    0xcd         0x12

x86系列的CPU都是little-endian的字節序。


關於2的補碼

問一個基本的問題。

負數在計算機中如何表示?

舉例來說,+8在計算機中表示爲二進制的1000,那麼-8怎麼表示呢?

很容易想到,可以將一個二進制位(bit)專門規定爲符號位,它等於0時就表示正數,等於1時就表示負數。比如,在8位機中,規定每個字節的最高位爲符號位。那麼,+8就是00001000,而-8則是10001000。

但是,隨便找一本《計算機原理》,都會告訴你,實際上,計算機內部採用2的補碼(Two's Complement)表示負數。

什麼是2的補碼?

它是一種數值的轉換方法,要分二步完成:

第一步,每一個二進制位都取相反值,0變成1,1變成0。比如,00001000的相反值就是11110111。

第二步,將上一步得到的值加1。11110111就變成11111000。

所以,00001000的2的補碼就是11111000。也就是說,-8在計算機(8位機)中就是用11111000表示。

不知道你怎麼看,反正我覺得很奇怪,爲什麼要採用這麼麻煩的方式表示負數,更直覺的方式難道不好嗎?

昨天,我在一本書裏又看到了這個問題,然後就花了一點時間到網上找資料,現在總算徹底搞明白了。

2的補碼的好處

首先,要明確一點。計算機內部用什麼方式表示負數,其實是無所謂的。只要能夠保持一一對應的關係,就可以用任意方式表示負數。所以,既然可以任意選擇,那麼理應選擇一種最方便的方式。

2的補碼就是最方便的方式。它的便利體現在,所有的加法運算可以使用同一種電路完成。

還是以-8作爲例子。

假定有兩種表示方法。一種是直覺表示法,即10001000;另一種是2的補碼錶示法,即11111000。請問哪一種表示法在加法運算中更方便?

隨便寫一個計算式,16 + (-8) = ?

16的二進制表示是 00010000,所以用直覺表示法,加法就要寫成:

 00010000
+10001000
---------
 10011000

可以看到,如果按照正常的加法規則,就會得到10011000的結果,轉成十進制就是-24。顯然,這是錯誤的答案。也就是說,在這種情況下,正常的加法規則不適用於正數與負數的加法,因此必須制定兩套運算規則,一套用於正數加正數,還有一套用於正數加負數。從電路上說,就是必須爲加法運算做兩種電路。

現在,再來看2的補碼錶示法。

 00010000
+11111000
---------
100001000

可以看到,按照正常的加法規則,得到的結果是100001000。注意,這是一個9位的二進制數。我們已經假定這是一臺8位機,因此最高的第9位是一個溢出位,會被自動捨去。所以,結果就變成了00001000,轉成十進制正好是8,也就是16 + (-8) 的正確答案。這說明了,2的補碼錶示法可以將加法運算規則,擴展到整個整數集,從而用一套電路就可以實現全部整數的加法。

2的補碼的本質

在回答2的補碼爲什麼能正確實現加法運算之前,我們先看看它的本質,也就是那兩個步驟的轉換方法是怎麼來的。

要將正數轉成對應的負數,其實只要用0減去這個數就可以了。比如,-8其實就是0-8。

已知8的二進制是00001000,-8就可以用下面的式子求出:

 00000000
-00001000
---------

因爲00000000(被減數)小於0000100(減數),所以不夠減。請回憶一下小學算術,如果被減數的某一位小於減數,我們怎麼辦?很簡單,問上一位借1就可以了。

所以,0000000也問上一位借了1,也就是說,被減數其實是100000000,算式也就改寫成:

100000000
-00001000
---------
 11111000

進一步觀察,可以發現100000000 = 11111111 + 1,所以上面的式子可以拆成兩個:

 11111111
-00001000
---------
 11110111
+00000001
---------
 11111000

2的補碼的兩個轉換步驟就是這麼來的。

爲什麼正數加法適用於2的補碼?

實際上,我們要證明的是,X-Y或X+(-Y)可以用X加上Y的2的補碼完成。

Y的2的補碼等於(11111111-Y)+1。所以,X加上Y的2的補碼,就等於:

X + (11111111-Y) + 1

我們假定這個算式的結果等於Z,即 Z = X + (11111111-Y) + 1

接下來,分成兩種情況討論。

第一種情況,如果X小於Y,那麼Z是一個負數。這時,我們就對Z採用2的補碼的逆運算,求出它對應的正數絕對值,再在前面加上負號就行了。所以,

Z = -[11111111-(Z-1)] = -[11111111-(X + (11111111-Y) + 1-1)] = X - Y

第二種情況,如果X大於Y,這意味着Z肯定大於11111111,但是我們規定了這是8位機,最高的第9位是溢出位,必須被捨去,這相當於減去100000000。所以,

Z = Z - 100000000 = X + (11111111-Y) + 1 - 100000000 = X - Y

這就證明了,在正常的加法規則下,可以利用2的補碼得到正數與負數相加的正確結果。換言之,計算機只要部署加法電路和補碼電路,就可以完成所有整數的加法。

(完)

轉載自:http://www.ruanyifeng.com/blog/2009/08/twos_complement.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章