2010-06-04 181 views
10

大概是一個非常簡單的答案,這非常簡單的問題,使用scanf函數:在while循環

我在讀「C的Primer Plus」由Pratta他不斷使用示例

while (scanf("%d", &num) == 1)... 

的是== 1真的有必要嗎?這似乎是一個可以只寫:

while (scanf("%d", &num)) 

這似乎是平等的測試是不必要的,因爲scanf函數的對象讀取和1將使while循環真正返回數字。是否有理由確保讀取的元素數量恰好爲1或完全是多餘的?

+0

是的,看起來相當多餘的我... – tzaman 2010-06-04 01:11:41

回答

22

在C中,0被評估爲假,其他一切都爲真。因此,如果scanf返回EOF(負值),則循環將評估爲true,這不是您想要的。

+0

EOF是這裏的關鍵元素,很好的捕獲。 – 2012-01-18 21:28:49

3

您正確理解C代碼。

有時,測試讀取的項目數量的原因是,有人希望確保所有項目在輸入與預期類型不匹配時被提前讀取而不是提前退出。在這種特殊情況下,這並不重要。

通常scanf是很差的功能選擇,因爲它不符合人類用戶交互輸入的需求。通常fgets和sscanf的組合產生更好的結果。在這種特殊情況下,這並不重要。

如果後面的章節解釋爲什麼某些編碼實踐比這個簡單的例子更好,那麼很好。但是,如果沒有,你應該傾銷你正在閱讀的書。

另一方面,您的替代代碼並不完全取代。如果scanf返回-1,那麼你的while循環將會執行。

+0

錯了,看到其他答案。 EOF命中或發生讀取錯誤時返回-1。 「 – 2010-06-04 03:25:40

+2

」錯誤,請參閱其他答案。「 - 你的意思是錯誤的,看到我自己的答案,因爲這就是爲什麼我寫了「如果scanf返回-1,那麼你的while循環將執行。」 – 2010-06-04 04:53:38

8

由於scanf在文件末尾返回值EOF(-1),因此寫入的循環是正確的。只要輸入包含與%d匹配的文本,它就會運行,並停止在第一個不匹配或文件結尾處。

這將一目瞭然已經更加清晰,如果scanf平均預期多於一個的輸入....

while (scanf("%d %d", &x, &y)==2) { ... } 

將退出循環,當它第一次無法匹配兩個值,無論是由於文件的文件結束的端部(scanf返回EOF(其爲-1))或在輸入匹配誤差(例如輸入xyzzy 42不匹配%d %d在第一次失敗,從而scanf停止並返回0 無需編寫要麼xy )當它返回小於2的某個值時。

當然,scanf而不是你的朋友,當解析來自正常人的真實輸入。處理錯誤案件有許多缺陷。

編輯:校正的錯誤:scanf返回EOF上文件結束,或一個非負整數計數它成功地設置的變量的數目。

關鍵是,由於C中的任何非零值爲TRUE,因此未能在這樣的循環中正確測試返回值可能會導致意外的行爲。特別是,while(scanf(...))是一個無限循環,除非遇到無法根據其格式轉換的輸入文本。

我不能強調得足夠強,scanf不是你的朋友。 fgetssscanf的組合可能足以進行一些簡單的解析,但即使如此,它也容易被邊緣情況和錯誤所淹沒。

1

雖然你是正確的,但並不是絕對必要的,有些人更喜歡它有幾個原因。

首先,通過與1進行比較,它變成了明確的布爾值(true或false)。如果沒有進行比較,您正在測試一個整數,這個整數在C中是有效的,但不是在以後的語言中(如C#)。其次,有些人會根據while([function])而不是while([return value])讀取第二個版本,並且通過測試一個函數會暫時混淆,當時明確的意思是測試返回值。

這可以完全是個人喜好的問題,就我而言,兩者都是有效的。

1

一個人可能會寫沒有明確的比較(雖然見JRL的答案),但爲什麼會呢?我想說,無比較條件只能用於具有明確布爾語義的值(例如,調用isdigit())。其他一切都應該使用明確的比較。在這種情況下(scanf的結果),語義顯然是非布爾型的,所以明確的比較是按順序的。

而且,通常可省略的比較通常是與的比較。當你想要省略與其他事物的比較(比如本例中的1)時,最好三思而後行,確保你知道自己在做什麼(再次參見JRL的答案)。

在任何情況下,如果可以安全省略比較並且實際上忽略它,則條件的實際語義含義仍然相同。如果這是您所擔心的問題,它對所得代碼的效率絕對沒有影響。