補一下CSAPP的筆記html
計算機中的整數主要包括兩種:有符號數與無符號數。程序員
其中有符號數的表示方法與傳統二進制一致。
假設有一個整數數據類型有w位。咱們能夠將位向量寫成
在這個編碼中,每一個xi都取值爲0或1。咱們用一個函數來表示B2Uw來表示:
無符號數的編碼方式實際上與咱們所知道的二進制編碼方式是一致的。惟一要注意的是無符號數的編碼具備惟一性,也就是說一個數字只能有一個無符號數編碼。這是由於B2Uw是一個雙射。函數
有符號數的編碼主要有三種方式:原碼、補碼與反碼。我曾經寫過一篇博客來進行探究,這裏不贅述。
關於補碼的由來和做用
須要說明的是:補碼也具備惟一性,原碼與反碼不具有這種性質,由於0在原碼與反碼中有兩種解釋。測試
C語言中提供了在不一樣數據類型中作強制類型轉換的方法,對於無符號整數與有符號整數之間的轉換方式,大多數系統上默認的是底層的位不變,由此咱們能推出有符號數與無符號數之間的轉換。
關於這些的轉換的的過程和原理,在此不贅述。這裏直接給出公式:
一個數的編碼方式從無符號編碼(補碼)轉換爲有符號編碼後的數值公式爲:編碼
若是有符號數的真值小於0那麼,把真值加上2w即爲其無符號真值,若是真值大於0,那麼不變。
咱們用一段C語言代碼舉例:設計
#include<stdio.h> #include<stdlib.h> #include<limits.h> int main(void) { int i = -1; unsigned int j = (unsigned int)i; printf("%u\n", j); printf("%u\n", UINT_MAX); system("pause"); }
VS2017下的運行結果:
數據類型int的大小爲8字節,32位,把-1轉換成無符號數須要加上232,結果爲232-1,正好爲無符號數編碼的最大值,因此與UINT_MAX的值一致。3d
直接給出公式:
C語言代碼測試實例:code
#include<stdio.h> #include<stdlib.h> #include<limits.h> int main(void) { unsigned int i = UINT_MAX; int j=(int)i; printf("%d", j); system("pause"); }
VS2017下的運行結果:
htm
須要說明的是,在VS2017的環境下,上面兩個程序通過測試即便不使用強制類型轉換也能夠獲得正確的結果,其一是C語言中若是發現左右兩邊數據類型不一致會自動把數據往左邊的類型轉換,其二是,printf中的格式說明符也會自動執行類型轉換,這裏使用強制類型轉換隻是爲了讓轉換看起來更加清晰。blog
因爲C語言對同時包含有符號和無符號數表達式的這種處理方式,出現了一些奇特的行爲。當執行一個運算時若是它的一個運算數是有符號的而另外一個是無符號的,那麼C語言會隱式地把有符號參數強制類型轉換爲無符號,並假設這兩個數都是非負的。
對於 <和> 這樣的關係運算符來講,它會致使非直觀的結果。咱們一樣用一個C語言程序來做爲測試:
#include<stdio.h> #include<stdlib.h> int main(void) { printf("%d", -1 < 0U); printf("%d",(unsigned)-1 > -2); }
VS2017運行結果:
第一個表達式中,因爲0是無符號數,因此-1默認變成無符號數,即爲232-1,這個數必然比0要大。因此第一個表達式爲假。
第二個表達式中,經過把-1強制轉換成無符號數,-1變爲232-1,-2變爲232-2,因此第二個表達式爲真。
有時咱們會把一個佔用空間較小的數據類型轉換爲佔用空間較大的數據類型(若是把佔用空間較大的數據類型轉換爲佔用空間較小的數據類型,可能會丟失數據,咱們通常不推薦這麼作)。
定義寬度爲w位的位向量:
和寬度爲w’的位向量:
其中w'>w。則:
要將一個無符號數轉換爲一個更大的無符號數數據類型,咱們只要簡單的在前面加上足夠的0便可,這種運算被稱爲零擴展。
定義寬度爲w位的位向量:
和寬度爲w’的位向量:
其中w'>w。則:
要將補碼數字轉換爲一個更大的數據類型,能夠執行一個符號擴展(sign-extension),在前面添加最高有效位的值。
具體證實略。
值得注意的點:在C語言中,把類型不一樣、大小不一樣的兩個數據類型相互轉換,先改變數據類型的大小,而後在執行類型轉換。
好比說:在C語言中,把一個short類型的變量轉換爲unsigned類型的變量,咱們要先把short類型的變量擴展到8個字節,而後再執行有符號數到無符號數的轉換。
一些特殊狀況下,儘管這樣作會帶來風險,但咱們仍然有時候會須要把一個高位的數據類轉換爲低位的數據類型,這時候咱們就須要截斷這個數字。
定義寬度爲w位的位向量:
而它截斷爲k位的結果爲:
令x=B2U_w(\vec x),x'=B2U_(\vec x'),則x'=x mod 2^k。
截斷爲k爲實際上就是對原數的真值用2^k取模。具體證實過程略。
要理解有符號數的截斷,咱們首先要明白,不管是有符號數仍是無符號數真正區別他們的不是他們的真值,而是他們的編碼方式,實際上不管是有符號數,仍是無符號數,在內存中都表示爲串二進制數,有了編碼對他們真值的解釋,他們才能表示不一樣的數據。
咱們都知道,截斷實際上就是截去前面冗餘的位,只留下咱們須要的位,既然無符號數和有符號數在內存中表示的方法實際上都是一串二進制數,咱們爲何不能夠把一個有符號數的位模式,看作是無符號數的編碼,用無符號數的方式將其截斷後獲得的真值,再用把無符號數轉換爲有符號數,最終獲得將有符號數階段的真值。
總而言之,有符號數編碼的截斷結果是:
有符號數到無符號數的隱式的強制類型轉換,每每會隱藏許多難以發現的錯誤,因爲這種強制類型轉換在代碼找那個沒有明確指示的狀況下發生的,程序員們每每會忽略掉它的影響。 C語言中雖然沒有明確規定無符號數的編碼方式,但大多數系統和機器都默認使用補碼來表示。而且對於無符號數而言,在不少程序設計語言中,並無這個類型,緣由很簡單,就是由於有符號數轉換爲無符號數會帶來許多難以察覺的問題。好比JAVA中就不含有無符號類型。