最近使用scanf發現了自己對scanf函數還是不太了解,主要出現在無意中出現的一個錯誤;
scanf正確的寫法是,scanf中以什么格式輸入變量,則變量的類型就應該是什么格式,如下面scanf輸入到變量的格式是%c形式,因此變量sum的類型必須是char型,要不存儲到sum中的數值會出錯;
注意:打印的時候是分別以%c、%d 的形式答應的
字符a的ASCII碼值是97
char sum; printf("請輸入一個字符:"); scanf("%c", &sum); printf("%c\n", sum); printf("%d\n", sum);
如果將sum定義成int類型,但是scanf以%c的格式賦值給sum,會出現什么樣的錯誤呢
int sum; printf("請輸入一個字符:"); scanf("%c", &sum); printf("%c\n", sum); printf("%d\n", sum);
看問題來了,為什么sum為int類型時;以%d輸出時明顯不對,%c輸出卻沒有問題?
%c格式輸出沒有問題說明scanf以%c格式輸入到sum的過程是沒有問題的,出現問題的原因是scanf內部實現的原理沒弄清楚
第一個首相想到,難道是一個字符/字符變量賦值給一個整形變量,再以%d形式打印時會出現這樣的問題嗎?
其實是不會的因為編譯器會進行自動轉換(隱式轉換),但還是看一下這種情況是什么樣子的
int sum; char p; p = 'a'; sum = p; printf("%c\n", sum); printf("%d\n", sum);
第二個猜測是,在scanf內部實現中以什么樣的格式賦值給變量時,就會以此格式對應類型的內存大小賦值給變量且,如果變量原本所占內存比scanf中所用格式的內存大,則多出的那一部分會被填充為1(為什么會猜測填充為1呢? 主要是上面以%d輸出是數值很大,並且為負值),下面寫代碼驗證一下
int sum; char *b; b = (char *)∑ *b = 'a'; printf("%c\n", sum); printf("%d\n", (char)sum); printf("%d\n", sum);
看上面sum中的值61前面全是c,c在16進制中為1100,61表示97,所以在低地址值是正確的,但在高地址處被填充的1100;並且紅色全出的數值和scanf出錯的那部分相同,所以scanf中內部實現原理是和上面代碼類似的。以char類型解釋;
即scanf(“%c”, &sum)時;內部會將 &sum 強制轉換成 (char *)類型,並且賦值給 char * 類型的變量b,然后用 *b 來接收輸入的值,也就改變了 sum中的值,但因為char字節小於int字節數,所以多余的字節會被其它值填充。
再看看上面隱士轉換情況多余的地址被填充了什么,為方便對照,再把代碼寫一遍;
int sum; char p; p = 'a'; sum = p; printf("%c\n", sum); printf("%d\n", sum);
隱士轉換多余字節被填充為0;所以隱士轉換不影響值(數值比較小時)的顯示,但可能會字節數不相等會影響結果精度等。