天天看點

C陷阱與缺陷中有關scanf的一道題

最近重讀了一下《C陷阱與缺陷》,發現一道題挺有意思的,這道題是一段簡單的代碼,如下所示:

#include <stdio.h>
int main(){
    int i;
    char c;
    for(i = ;i < ;i++){
        scanf("%d",&c);
        printf("%d",i);
    }
    printf("\n");
    return ;
}
           

注意scanf(“%d”,&c),在讀入時分兩種情況:

(1)讀入時是數字,此時程式會陷入死循環,讀入一個數字就輸出一個0,造成這種情況的原因是在字元c的位址上,程式以“%d”存儲了一個整型資料,而字元c本來是一個位元組,假設int占四個位元組(我的機器上這樣的),那麼字元c的位址附近的記憶體會被覆寫。下面以圖形化的形式說明這種情況

(gdb調試程式)

若目前時刻i = 1,列印i的位址是0xbffff3fc,c的位址是0xbffff3fb,未執行scanf時的記憶體内容如下

内容(從記憶體位址開始的四個位元組内容) 記憶體位址
0x01 0x00 0x00 0x00 0xbffff3fc
0x01 0x01 0x00 0x00 0xbffff3fb

執行了scanf以後(假設讀入了數字5),記憶體的情況如下圖是以

内容(從記憶體位址開始的四個位元組内容) 記憶體位址
0x00 0x00 0x00 0x00 0xbffff3fc
0x05 0x00 0x00 0x00 0xbffff3fb

顯然,在執行完scanf後,從位址0xbffff3fb開始的四個位元組被0x05 0x00 0x00 0x00 覆寫了,變量i的低端部分被置0,而i的高端部分本身就是0,那麼此時i的值就變成了0,i的值始終都在0和1之間,就形成了最後的死循環。

(2)讀入的是非數字的字元,那麼scanf就會遇到讀入資料與scanf期待的%d類型不符,導緻stdin流出現堵塞,下次循環将不再讀入資料除非清空stdin緩沖區,最終導緻的結果就是直接輸出01234