2017-06-07 68 views
1
#include <stdio.h> 

int main() 
{ 
    int sum=0; 
    char s[10]; 

    while(scanf("%[^\n]s", s)!=EOF) 
    { 
    printf("%s", s); 

    } 

    return 0; 
} 

此while循環落入任何字符串輸入的無限循環。爲什麼這個c程序陷入無限循環?

+1

我們應該有一些*初學者從'scanf函數導遠()'*。對於初學者來說,'s'是錯誤的,因爲'[]'已經是一個轉換,'scanf()'會在它後面尋找一個字面的's'。 –

+0

然後,當你解決這個問題時,你必須處理在第一個'scanf()'後面留下的換行符。 –

+0

第二次拒絕'scanf(「%[^ \ n] s」,s)''中的換行符'然後相同。 – BLUEPIXY

回答

6

鑑於

while(scanf("%[^\n]s", s)!=EOF) 
{ 
    printf("%s", s); 
} 

你問「?爲什麼這個C程序陷入無限循環」

因此,您必須瞭解scanf()的工作原理。 scanf()使用您的格式字符串解析來自stdin的輸入,這是一個流,它可以按字符提供字符。

假設你已經在stdin如下:

foo\n 
bar\n 
<<EOF>> 

現在您的格式字符串有這種轉換%[^\n]這意味着「匹配任何東西,但換行,並複製到一個字符緩衝區」。 s後面的文字只是一個字面s,因爲它前面沒有% ...所以如果有的話它會與字面s相匹配。換句話說,這裏沒關係。

現在,第一次打電話給你的scanf(),它將匹配foo並使用它。它返回1,因爲它匹配了一個元素。在此之後,stdin看起來是這樣的:

\n 
bar\n 
<<EOF>> 

注無與倫比的換行符仍然存在。您的下一個電話將再次以格式字符串開頭,以匹配,但換行符,但下一個字符是換行符。 scanf()什麼都不匹配,因此返回0。它不能返回EOF,因爲如您所見,EOF尚未到達。這是你的無限循環。

所以,你必須修復你的格式字符串。首先,刪除不匹配任何東西的s。它在這裏並沒有真正受到傷害,但它仍然是錯誤的。然後,您可以利用空白匹配scanf()。空格是空格,標籤換行。如果你的格式字符串包含空格,scanf()將與一樣多的空格匹配(這也可以不是)。因此,一個簡單的辦法是隻啓動格式字符串用空格,這將「吃」的遺留的換行符:

while(scanf(" %[^\n]", s)!=EOF) 
{ 
    printf("%s", s); 
} 

現在這仍然是危險代碼,因爲[^\n]匹配任何量字符,只要沒有換行符,但是緩衝區只有9個字符的空間加上終止0。所以,你必須告訴scanf()不超過該限制匹配更多,這可以通過將%和轉換規範之間的數量來完成:

while(scanf(" %9[^\n]", s)!=EOF) 
{ 
    printf("%s", s); 
} 

該代碼會做你想做的事,安全的方式,但假設你想匹配更具體的東西,例如數字,你會得到一些意想不到的輸入:通過等待EOF,你會再次有你的無限循環,因爲有輸入不匹配,所以你永遠不能達到EOF(如上面與無與倫比的換行符)。因此,請始終檢查成功匹配的數量。在這裏,您希望只有一個匹配,因此該循環應該是這樣的:

while (scanf(" %9[^\n]", s) == 1) 
{ 
    printf("%s", s); 
} 

在實踐中,讀取整行,你不需要scanf(),只是用fgets()會更容易些。你的代碼可能看起來如此簡單(新行會被讀入s以及):

while (fgets(s, 10, stdin)) 
{ 
    printf("%s", s); 
} 

注意第二個參數10這裏:fgets()會自動考慮終止0角色,所以你只要給它的大小你的緩衝區。

只是最後提示:這可能是即使不使用printf()簡單時沒有格式化的事:

while (fgets(s, 10, stdin)) 
{ 
    fputs(s, stdout); 
} 
+0

正如你所說,最好使用'fgets()'。其次,使用'scanf()'是比較返回值與預期賦值的數量,而不是'EOF':'while(scanf(「%9 [^ \ n]」,s)== 1){/ * ... * /}' – pmg

+0

@pmg謝謝,很好,我延長了答案。 –

-1
int main() 
{ 
    int sum = 0; 
    char s[10]; 

    int ret = scanf("%[^\n]s", s); 
    while (ret != EOF) 
    { 
     printf("%s", s); 
     memset(s,0,10); 
     ret = scanf("%[^\n]s", s); 
    } 

    return 0; 
} 

隨着調試,你可以看到第二個ret值在while塊爲0,也就是說,並不等於EOF,所以無限循環。

人scanf函數:

返回值

成功完成後,這些函數將返回成功匹配和分配的輸入項目的數量;如果發生早期匹配失敗,此數字可能爲零。

這意味着其他scanf匹配失敗,除了第一個。

但是爲什麼?

stdio有緩存,匹配只讀取stdin中的「\ n」,但是當你輸入「Enter」時,它會輸入「\ n \ r」,所以「\ r」會在stdin緩存併成爲其他scanf的輸入值,但無法匹配輸入,所以匹配失敗。並且scanf返回0,並且轉到無限循環。

但是,如果您只爲每個輸入輸入「\ n」,那沒關係!

你可以按「Alt」,同時按下「0」+「1」+「0」,釋放「Alt」,控制檯將只輸入「\ n」結果,程序會OK,不會進入無限循環!

這是我的測試結果:enter image description here

+1

如果使用「scanf(」%[^ \ r \ n] s「,s);」 scanf也可以從無限循環停止,但不能再輸入。 – Jimmy

+1

這是完全錯誤的。請注意'\ n'是C **中的換行符**,並且在文本模式下閱讀時,任何特定於平臺的換行符都被轉換爲'\ n'。另請注意,windows換行符是'\ r \ n',而不是'\ n \ r'。它繼續:'%[^ \ n]'不匹配換行符,它匹配*任何**,但**換行符* –

+0

我不確定現在,我嘗試使用此scanf:「scanf(」 [^ \ n] s「,s);」 (開頭有空格),結果與「scanf(」%[^ \ r \ n] s「,s)一樣;」它可以停止循環,但不能再輸入。 – Jimmy