在C語言程式中,將變數賦值為-1,再用printf列印,為什麼輸出一個很大的數?

在C語言程式中,將變數賦值為-1,再用printf列印,為什麼輸出一個很大的數?IT劉小虎2019-07-28 20:44:37

前兩天,我在我的圈子裡發了一個小問題,剛好和題主的問題很吻合,相關的C語言程式碼如下,這段程式會輸出什麼呢?

在C語言程式中,將變數賦值為-1,再用printf列印,為什麼輸出一個很大的數?

題外話

在分析這個問題之前,先說些題外話。有程式設計師認為研究這樣的程式碼沒有意義,無異於孔乙己的“茴”字有幾種寫法。

這個問題其實並不是我空想出來的。

在C語言程式中,將變數賦值為-1,再用printf列印,為什麼輸出一個很大的數?

最近,我的一個同事被他的C語言程式 bug 困擾了好幾天,始終無法找到問題究竟出在哪裡,於是找我,我看到他的程式碼居然混用無符號變數和有符號變數,於是就提醒他注意這個方面,後來發現果然是這個原因。他的問題涉及到比較複雜的專案,完整的複述一遍不太現實,於是我把他的問題精簡一下,就構成了上述C語言程式碼段。

事實上,很多公司招聘時,都有一些面試題或者筆試題看起來很怪異,很不符合標準的開發規範,於是有些程式設計師就認為做這樣的面試題是完全沒有意義的,甚至覺得做這些題目是一種侮辱。

其實換個角度想想,這些題目很能考察一個人的基本功,它們很可能來自公司內部的某個專案的某次重大 bug。C語言是一門極其重視基本功的程式語言,這些題目很能查漏補缺。

分析

現在來考慮上面這段C語言程式碼,我們編譯並執行它,得到了下面的輸出:

在C語言程式中,將變數賦值為-1,再用printf列印,為什麼輸出一個很大的數?

C語言程式的輸出出乎了一些朋友的預料,-1 容易理解,255 是怎麼回事呢?

首先要明白的是,在計算機中,整數通常採取補碼的形式儲存。負數的補碼等於其反碼+1,負數的反碼符號位不變,數值為按位取反。對於 signed char 型變數,大部分C語言編譯器都是由 8 個 bit 組成的,最高一個 bit 通常表示符號位。

所以對於 -1,其原碼原本是 0b10000001,但是計算機內部儲存該數值時,是以補碼形式儲存的。-1 的補碼等於反碼+1,也即 0b11111110 +1 = 0b11111111 = 0xff。

在C語言程式中,將變數賦值為-1,再用printf列印,為什麼輸出一個很大的數?

到這裡就清楚了,變數 b 在記憶體裡的 8 個 bit 都是 1,它是一個 unsigned char 型的變數,最高 bit 也表示數值,也即 b 等於 255。

現在再來分析變數 c 和變數 d 的值,它倆都是有符號型的 int 型。按理說,a 和 b 在記憶體中的佈局是一樣的,都是 8 個 bit 的 1,為什麼傳遞給 c 和 d 就不一樣了呢?

其實C語言在處理 c = a; 和 d = b; 這兩句賦值語句時,有一個過程沒有顯式的表現出來,即“整形提升”。以 c=a; 為例,因為 c 和 a 的資料型別不同,所以C語言在處理賦值時,為了不丟失精度,會將 a 中的數值也強制轉換為 int 型。

a 中的數值是 -1,提升為 int 型後依然是 -1,而不是 0x000000ff(255,這裡假設 int 型別佔用 4 位元組記憶體空間)。至於變數 d 的值,就更簡單了,就是簡單的賦值而已。

小結

本節討論的問題雖然很簡單,但是仍然有很多人做錯,這其中也包含我工作多年的同事。C語言是一門極其重視基本功的程式語言,事實上,本節涉及的知識點非常基礎,無非就是原碼補碼,以及整型提升的相關知識。

在C語言程式中,將變數賦值為-1,再用printf列印,為什麼輸出一個很大的數?