爲什麼所定義的數據類型與輸出類型不符,輸出的數據爲一個“垃圾”數字?這個數字能計算嗎?

爲什麼所定義的數據類型與輸出類型不符,輸出的數據爲一個“垃圾”數字?這個數字能計算嗎?

答案是:可以的。下面所有案例爲unsigned int定義而用%u或%d輸出的情況討論與分析

因爲%u可以輸出unsigned int unsigned short 和unsigned char
故猜想**%u將內存中的數據先轉化爲4字節,再按照二進制數據進行無符號處理並輸出**。該猜想在下面實例中得以驗證。

以下內容所涉及到的長短數據擴展請參考細文:http://blog.sina.com.cn/s/blog_6adcb3530101cmsd.html

這裏我們簡單講述下輸出a時的情況,爲下面輸出-a作鋪墊。

這裏寫圖片描述

如圖中定義的unsigned char a =169。先將169轉化爲對應的二進制數,也就是 1010 1001 由於169爲正數,所以它的原碼應該爲0 1010 1001 。故它的補碼等於它的原碼0 1010 1001。由於unsigned char類型爲1字節8位,故將169的補碼取出後8位捨去最高位的符號位 0 ,存儲在a這個無符號字符型變量中。
故a存儲的是
1010 1001(169的8位數補碼)

在直接以%u輸出a的過程中:
a存儲的補碼 1010 1001 可如何將這個1字節數據,以4字節輸出呢?這時要把1字節拓展爲4字節:
這裏寫圖片描述

//1010 1001  擴展-> ???? ???? ???? ???? ???? ???? 1010 1001("?"處爲尚未填充區)
//由於從unsigned char類型擴展到int時,填充區的值都是0(如見上圖unsigned char轉化爲long類型)
//故擴展之後的4字節unsigned char a 的補碼爲:  0000 0000 0000 0000 0000 0000 1010 1001 

當%u與%d接受到這個補碼時:
%u當做無符號類型解析: 該補碼 0000 0000 0000 0000 0000 0000 1010 1001 32位全爲數值位,並且爲無符號類型解析,則該補碼就是其a擴寬後的原碼。
而%u是以十進制輸出的,故該數值爲這個32位二進制數的十進制數值:169;
%d當做有符號類型解析: 該補碼0000 0000 0000 0000 0000 0000 1010 1001,在%d面對它時,會將其的第一個二進制位看作是符號位,在這裏也就是 0 ,其後面的31位將作爲數值位。而%d以十進制輸出,故該31位的二進制數的十進制數值爲:169,由於符號位爲0,表示爲正數。繼而%d輸出a爲:169。

這裏開始重點講一下,如果printf輸出的是 -a 的情況
這裏寫圖片描述

同樣的,當數據即將傳給printf之前,a在內存中存儲的169補碼爲 1010 1001
傳入printf
%d與%u解析之前: 要將-a傳遞給%d與%u,就先要將-a表示出來。這時要在a所存儲的補碼前面加一個符號位" 1 "用來爲表示 -a 做準備 ,則現在爲: 1 1010 1001 之後,怎樣將這個二進制數據轉爲 -a 的呢?這裏可能是將加符號位後的二進制:1 1010 1001 看成是“ -a ” 的原碼,然後要將“ -a ”的這個原碼轉換爲補碼後,傳遞給%u與%d。 由於-a 原碼的符號位爲“ 1 ”,故其補碼爲**1 0101 0111 ** (符號位不動,其它8位取反爲“1 0101 0110 ”後 +1 成爲 " -a "的補碼 )
之後將這個補碼擴寬到32位( -a 相當於一個新char“帶符號類型”,所以擴寬的時候以符號位填充)
由於符號位爲“ 1 ”,故4字節補碼錶示爲:1 111 1111 1111 1111 1111 111 1 0101 0111

現在將這個補碼分別以%u和%d輸出:
%u十進制輸出時: 由於%u解析的時候不考慮符號位,故該32位全爲數值位,由於%u考慮的無符號類型,故%u認爲這是一個正數,即原碼=補碼。故%u輸出的是該32位所代表的數值: 4294967127
%d十進制輸出時: 由於%d解析的時候要考慮符號位,並且要將補碼轉換爲原碼輸出。 則步驟如下:
補碼減一後,符號位不變,其它31位取反,繼而原碼爲:1 000 0000 0000 0000 0000 000 0 1010 1001 該31位二進制數值與其符號位輸出的是:-169;

總結以上規律,得有以下結論:在unsigned char定義一個整數或負數a時,面對以%u與%d輸出 -a ,有以下步驟:
1:對unsigned char變量所存儲的補碼加一個符號位,該符號位的數值爲“ 1 ”(可能是用其表示負號而添加的)
2:對於這個新的二進制數據,把它當做新數據(比如-a)的原碼,然後轉化爲補碼。
3:進行短數據擴寬(加入符號位1後類似於一個char類型,填充時以“ 1 ”進行填充)
4:按照 %u還是 %d,以對應規則,轉化爲原碼並輸出其十進制。
注意:上面是輸出 -a(無論a爲正爲負),而輸出a時,在截斷8位數據後的補碼直接進行填充,並按百分號類型輸出
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
現將上面的結論再應用於實踐中驗證一下。

這裏寫圖片描述


a=-69

-69的原碼爲: 1100 0101 (第一個1爲符號位)
由於符號位爲1 ,故取其補碼: 1011 1011 則a存儲的補碼爲: 1011 1011

輸出a
a的補碼被填充後爲0 000 0000 0000 0000 0000 0000 1011 1011(按unsigned char擴展爲 int,全部填充0)
擴展填充0後,第一位0相當於新符號位,原來的符號位成爲了一個數值位,故%d與%u輸出同爲:187
(補碼=原碼= 0 000 0000 0000 0000 0000 0000 1011 1011)

輸出 -a
**傳入printf:應用結論第一步:**在第9位加入一個符號位 1 1011 1011
第二步: 將這個二進制數據當作一個新原碼,則它的補碼爲:1 0100 0101
**第三步:**按照char 到 int 類型擴展:1 111 1111 1111 1111 1111 111 1 0100 0101
第四步 %u 將該32位二進制數據轉化爲十進制爲: 4294967109
%d 將該補碼換成原碼爲:1 000 0000 0000 0000 0000 000 0 1011 1011 輸出爲:-187


a=-169

這裏寫圖片描述

-169的原碼爲: 1 1010 1001(第一個1爲符號位)
由於符號位爲1 ,故取其補碼: 1 0101 0111 則a存儲的補碼爲: 0101 0111 (截取8位,符號位溢出)

輸出a
a的補碼被填充後爲0 000 0000 0000 0000 0000 0000 0101 0111(按unsigned char擴展爲 int,全部填充0)
擴展填充0後,第一位0相當於新符號位,原來的符號位成爲了一個數值位,故%d與%u輸出同爲:87
(補碼=原碼= 0 000 0000 0000 0000 0000 0000 0101 0111)

輸出 -a
**傳入printf:應用結論第一步:**在第9位加入一個符號位 1 0101 0111
第二步: 將這個二進制數據當作一個新原碼,則它的補碼爲:1 1010 1001
**第三步:**按照char 到 int 類型擴展:1 111 1111 1111 1111 1111 111 1 1010 1001
第四步 %u 將該32位二進制數據轉化爲十進制爲: 4294967209
%d 將該補碼換成原碼爲:1 000 0000 0000 0000 0000 000 0 0101 0111 輸出爲:-87


a=69

這裏寫圖片描述

69的原碼爲: 0100 0101(第一個0爲符號位)
由於符號位爲0 ,故其補碼=原碼:0100 0101

輸出a
a的補碼被填充後爲0 000 0000 0000 0000 0000 0000 0100 0101(按unsigned char擴展爲 int,全部填充0)
擴展填充0後,第一位0相當於新符號位,原來的符號位成爲了一個數值位,故%d與%u輸出同爲:69
(補碼=原碼= 0 000 0000 0000 0000 0000 0000 0100 0101)

輸出 -a
**傳入printf:應用結論第一步:**在第9位加入一個符號位 1 0100 0101
第二步: 將這個二進制數據當作一個新原碼,則它的補碼爲:1 1011 1011
**第三步:**按照char 到 int 類型擴展:1 111 1111 1111 1111 1111 111 1 1011 1011
第四步 %u 將該32位二進制數據轉化爲十進制爲: 4294967227
%d 將該補碼換成原碼爲:1 000 0000 0000 0000 0000 000 0 0100 0101 輸出爲:-69



本文考慮的四種情況是:

169(8位存儲的最高位被數值位1佔用(符號位的0被溢出))
-169(8位存儲的最高位被數值位1佔用(且符號位的1被溢出))
69(8位存儲數值位沒有達到最高位(符號位爲0且未被溢出))
-69(8位存儲數值位沒有達到最高位(符號位爲1且未被溢出))

初學者拙見,有錯誤的地方望指正。

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