在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:輸入/輸出地址。
參考資料
https://tieba.baidu.com/p/7250200456
《C語言程式設計現代方法-第二