2010-10-25 62 views
2


當我寫這一點,編譯和運行:scanf失敗爲什麼?

int x; 
scanf ("%d", &x); 
while (x!=4) { 
    scanf ("%d", &x); 
} 

和插入字符或雙號小於4時進入無限循環。
當插入雙倍大於4時,它終止。
任何解釋?

回答

9

C language standard (n1256)

7.19.6.2 fscanf函數
...
4 fscanf函數依次執行格式的每個指令。如果指令失敗,如下所述,函數返回。故障被描述爲輸入故障(由於出現編碼錯誤或輸入字符不可用)或匹配故障(由於輸入不正確)。
...
7作爲轉換規範的指令定義了一組匹配的輸入序列,如下面每個說明符所述。 A轉換規範在下面的步驟執行:

8輸入的空白​​字符(由isspace爲函數指定)被跳過,除非 說明書包括 [Ç,或 Ñ說明符。 250)

9的輸入項目是從該流讀取,除非本說明書中包括 Ñ說明符。輸入項被定義爲輸入字符的最長序列,其不超過任何指定的字段寬度,並且是匹配輸入序列的或者是匹配輸入序列的前綴。 251)輸入項目保持未讀狀態後的第一個字符(如果有的話)。如果輸入項目的長度爲零,則指令的執行失敗;除非文件結束,編碼錯誤或讀取錯誤阻止了來自流的輸入,否則此條件是匹配失敗,在這種情況下,這是輸入失敗。

10除了在 說明符時,輸入項(或者,在一個 %N指令,輸入字符的計數的情況下)的情況下被轉換成適合於該轉換指定一個類型。 如果輸入項目不是匹配序列,則指令的執行將失敗:此條件是匹配失敗。除非通過*指定了分配抑制,否則轉換結果將放置在尚未收到轉換結果的format參數之後的第一個參數指向的對象中。如果此對象沒有適當的類型,或者轉換的結果不能在對象中表示,則行爲是未定義的。

第10段中增加了重點。%d轉換說明符預計將輸入文本格式化爲十進制整數。如果不是,則轉換將失敗,導致轉換失敗的字符將保留在輸入流中。對scanf()%d轉換說明符的進一步調用將扼殺在同一個字符上。

scanf()返回成功分配的數量;你需要檢查這個結果,看看是否轉換成功,就像這樣:

int x = 0; 
while (x != 4) 
{ 
    int result = scanf("%d", &x); 
    if (result != 1) 
    { 
    printf("Last call to scanf() failed; exiting\n"); 
    break; 
    } 
} 

不幸的是,你仍然有錯誤的輸入卡的輸入流。有很多處理這個問題的策略。你可以用getchar刪除有問題的字符,然後再試一次:

while (x != 4) 
{ 
    int tmp; 
    if (scanf("%d", &tmp) == 0) 
    getchar(); 
    else 
    x = tmp; 
} 

或者你可以嘗試讀取到下一個新行,假設所有剩餘輸入b0rked:

while (x != 4) 
{ 
    int tmp; 
    if (scanf("%d", &tmp) == 0) 
    while (getchar() != '\n') 
     ; 
    else 
    x = tmp; 
} 

或者你可以嘗試讀取輸入作爲文本轉換使用strtol()(我的優選技術)爲整數:

char input[SOME_SIZE]; 
int x = 0; 
... 
while (x != 4) 
{ 
    if (fgets(input, sizeof input, stdin)) 
    { 
    char *check; 
    int tmp = (int) strtol(input, &check, 10); 
    if (!isspace(*check) && *check != 0) 
    { 
     printf("%s is not a valid integer: try again\n", input); 
    } 
    else 
    { 
     x = tmp; 
    } 
    } 
    else 
    { 
    printf("Read error on standard input\n"); 
    break; 
    } 
} 

這是更多的工作,但它可以讓你在之前捕獲不好的輸入,它被分配到x

+0

投票的參考和getchar()解決方案 – Aboelnour 2010-10-25 19:37:05

+0

只是一個小小的評論。忽略部分輸入實際上是一種非常糟糕的做法。你應該首先報告出現問題的錯誤,而不要繼續閱讀。 – 2010-10-25 19:45:10

-2

您傳遞給scanf的%d告訴它解析一個int。當你輸入一個double時,它不能解析既不將分析數據存儲在x變量中,因爲double在32位機器上使用64位而在32位上使用int,從而產生了一個Seg。故障或隨機附帶影響。

+2

-1 scanf讀取** text **。如果你輸入一個小數點(或一個指數,或任何*錯誤*的東西),scanf因不適當的字符而停止。 – pmg 2010-10-25 15:40:48

6

您不檢查scanf是否確實成功,因此您將遇到錯誤。每個循環,scanf將嘗試讀取和失敗。

scanf返回因此的成功讀取項目數修改環路這樣的事情while (x!=4) { if (scanf("%d",&x) != 1) break; }

+0

+1爲正確的方式使用'scanf' – pmg 2010-10-25 15:58:27

+0

投票正確使用scanf() – Aboelnour 2010-10-25 19:38:17

5

當scanf函數在輸入流中的特定位置停止掃描,它永遠不會提前流,所以下一個scanf函數會再試一次同樣的錯誤......又一次......又一次

 
input: 42 23 foo ... 
scanf:^
x  42 
scanf: ^
x  23 
scanf: ^
x  unusable 
scanf:  ^
x  unusable 
scanf:  ^
x  unusable 
scanf:  ^
x  unusable 
scanf:  ^
x  unusable 
+0

如何,如果我輸入一個雙數小於4它進入一個無限循環。當插入雙倍大於4時終止。 – Aboelnour 2010-10-25 15:43:00

+0

我無法解釋這種行爲。當x不是4時循環停止。但是,在失敗的scanf後使用x是Undefined Behavior:任何事情都可能發生。 – pmg 2010-10-25 15:47:27

+2

@Aboelnour如果你輸入'4.29419541951',它會讀取'4'作爲一個整數並退出。如果輸入'3.29419541951',它將讀取'3',然後在下一個循環中失敗,因爲第一個字符是'.'。你需要檢查返回值。檢查我的答案中的代碼。 – 2010-10-25 15:51:28