2012-06-18 63 views
3

我碰到下面的代碼來:C:在scanf行爲for循環

int i; 
for(; scanf("%s", &i);) 
    printf("hello"); 

按我的理解,如果我們提供的整數輸入scanf將在閱讀不成功,因此返回0,所以環形不應該運行一次。然而,它通過接受所有類型的輸入作爲成功讀取而無限運行。

有人會好心解釋這種行爲嗎?

+0

請參閱我的答案頂部的編輯。 :) – huon

回答

9

這是int的錯誤格式說明符:應爲"%d"

它試圖將一個字符串讀入一個int變量,可能會覆蓋內存。由於指定了"%s",因此將讀取所有輸入,因此scanf()會返回大於零的值。

2

(編輯:。我覺得這個答案應該被接受Upvoted可能,但不接受它並不能解釋無限循環可言,@hmjd確實是)

(這實際上並沒有回答這個問題,其他的答案做到這一點,但它是有趣和良好的就知道了。)

由於hmjd說,使用scanf這樣會覆蓋內存("smash the stack"),因爲它開始寫i在內存,然後繼續前進,即使在i佔用的4字節內存(或8字節,在64位平臺上)之外。

爲了說明,考慮下面的代碼位:

#include<stdio.h> 

int main() { 
    char str_above[8] = "ABCDEFG"; 
    int i; 
    char str_below[8] = "ABCDEFG"; 

    scanf("%s", &i); 

    printf("i = %d\n", i); 
    printf("str_above = %s\nstr_below = %s\n", str_above, str_below); 

    return 0; 
} 

編譯和運行它,並輸入1234567890產生以下輸出:

i = 875770417 
str_above = 567890 
str_below = ABCDEFG 

幾點:

  • i與整數幾乎沒有對應關係(它與字符'1',...,'4'的值以及系統的字節順序有關)。
  • str_above已被修飾以通過scanf:字符'5',...,'0''\0'已溢出的記憶保留i塊的端部與已被寫入爲str_above保留的存儲器。
  • 堆棧已被「向上」粉碎,即str_above稍後存儲在內存中,而i以及str_below存儲在內存中。 (換一種方式&str_above > &i&str_below < &i。)

這是爲「緩衝區溢出攻擊」,其中在棧上的值是由過多的數據寫入到一個數組修改的基礎。這就是爲什麼gets是危險的(並且應該使用從不使用)並且使用scanf與通用的%s格式說明符也不應該完成。