在scanf函數中佔位符使用錯誤而產生的一些錯誤

出現的問題

  在做編程題的的時候,遇到了一個很奇怪的錯誤,出問題的代碼如下:

 1 #include <cstdio>
 2 using namespace std;
 3 
 4 int main() {
 5     int c;
 6     bool b;
 7 //  printf("%p %p", &c, &b);    // c的地址是:66fe1c,b的地址是:66fe1b
 8     scanf("%d %d", &c, &b);
 9     printf("c = %d b = %d", c, b);
10     
11     return 0;
12 }

這是運行的結果:

  是不是覺得很奇怪?明明給變量c和變量b輸入的是10和1,可是在輸出的結果中,c的值為0。這是為什麼呢?其中原因涉及到內存。大概的解釋是,我通過%d佔位符來為bool型變量b輸入值,而系統為bool型的變量分配1個位元組大小的內存,又因為通過%d輸入的值是佔4個位元組大小的,所以由於變量b的內存不夠,輸入的值會佔用到變量c的內存並且覆蓋原來存儲的值。下面,我將通過變量所對應的內存中發生的變化進行詳細的解釋。

詳細解釋

  通過打印輸出變量c,b的地址,我們來繪製出相應的內存條:

 

  注意,由於我們在定義變量c,b時沒有進行初始化,系統會為變量隨機分配值,所以內存條應該是有相應的十六進制的值的,但圖中為了方便,表示空白省略其中相應的值。

  為了能夠更好地表示出其中的錯誤,我們會輸入比較大的數字。

  這裡我們還是先為變量c輸入10,之後十進制10會轉變為十六進制00 00 00 0A,倒着存儲到變量c的內存中:

  在為變量b輸入值之前,我們需要知道,在C++中,bool型變量的位元組大小為1個位元組,也就是sizeof(bool) == 1,但我們在scanf函數中通過%d佔位符來為變量b讀取值,又知道通過佔位符%d的值是佔4個位元組的大小的,而b變量的內存大小為1個位元組,很明顯,是存儲不了4個位元組大小的值的,所以只能通過佔用變量c的內存來存儲(準確來說是佔用了變量c的前3個位元組的內存),導致原本存儲在變量c內存中的值被覆蓋。

  現在,我們為b變量輸入180079837,再按照上述的說法,來看看內存發生了什麼變化:

  首先十進制180079837會轉變為十六進制0A BB CC DD,倒着存儲到變量b的內存中,又因為變量b的內存只有1個位元組的大小不夠存儲,所以會在接着的3個位元組大小內存來存儲,這就會導致屬於變量c的內存被佔用,我們輸入的新值覆蓋了原本的值。

  沒錯,這就是問題所在。現在我們來打印輸出看看他們的值:

  嗯…又是些很奇怪的值,這是怎麼讀出來的呢?

  首先來看變量b,雖然我們為變量b輸入的值是4個位元組的大小,但由於變量b的所佔的位元組大小為1,所以系統自會只讀66fe1b這個地址存放的值,十六進制DD對應着十進制的211,所以實際上b變量所存儲的值是211。

  然後是變量c,因為變量c是int型變量,佔用位元組大小為4,所以系統會從66fe1c這個地址開始往後讀4個位元組大小的內存,這4個位元組的內存就存儲着變量c的值,我們讀的順序應該是00 0A BB CC,對應着十進制703436,所以變量就存儲着703436,並打印輸出之。

  通過,上面的解析,我們現在應該明白了一開始為什麼我們輸入 10 1 會出現 c = 0, b = 1 的情況了,就是下圖的情況。

  所以有什麼解決的方法呢?首先,bool型沒有專門對應的佔位符。所以如果想通過scanf函數來為bool變量進行輸入,可以把該變量定義為int型並通過佔位符%d進行輸入,又或者是通過枚舉來定義。而如果是C++的話,就更簡單了,用cin來對bool型變量進行輸入。

 常用佔位符

%d:輸入/輸出十進制整數。

%o:輸入/輸出八進制整數。

%x:輸入/輸出十六進制整數。

%u:輸入/輸出無符號型整數。

%hd:輸入/輸出十進制短整數。

%ld:輸入/輸出十進制長整數。

%lld:輸入/輸出十進制長長整數。

%ull:輸入/輸出十進制無符號長長整數。

(如果要輸入/輸出八進制或十六進制的數,只需要把d改為o或x)

%f:輸入/輸出單精度浮點數。

%lf:輸入輸出雙精度浮點數。

%e:輸入/輸出科學計數。

%c:輸入/輸出一個字符。

%s:輸入/輸出字符串。

%p:輸入/輸出地址。

參考資料

//tieba.baidu.com/p/7250200456

《C語言程序設計現代方法-第二版》