這時候先學習 c++ 的好處就來了, 不需要先寫可怕的 malloc 來處理這件事情, 用 std::vector 就可以搞定, 如果你不是用 c++ 而是學 c, 那就得先刻一個類似 vector 的資料結構, 通常這時候也應該會寫 list, 剛好派上用場。
初學的時候一般會用一個折衷的辦法, 先輸入一個數字, 例如 5, 代表之後要輸入 5 筆資料, 再用 malloc 5 個 int 大小的記憶體區間, 厄, 不太好是吧, 還不如一次就把這些觀念帶上, 先用 c++ vector, 再用 c 自己實做一個 vector/list, 這本來就是本科系無法迴避的課題。scanf 可沒想像的簡單。如果是我來教學, 我會這麼做, 讓初學者知道有這樣的資料結構, 等學習一段時間之後, 再去實做那種資料結構。
我用 c++ 就偷懶一下了。scanf 對 c 初學者來說, 門檻真的太高了, 它比想像中的還要難。
另外一個類似的問題, 如果你要輸入一個字串, 要宣告 char str[10]; 確定這個夠嗎? 如果輸入的字串有 20 char 怎麼辦? POSIX.1-2008 親切的提供了 m 來處理這個問題, 參考 list 2 的用法, 然而在更早之前 GNU 提供了 a, 所以有點混亂, 更麻煩的是 a 現在提供給浮點數用, 而且成為標準, 但如果你用的是夠新的 c 編譯器, 應該有 m 可以用。windows/vs, mac/clang 請自行確認有沒有 m 可以用。請記得不用時要 free 這個 str, 另外傳給 scanf 時, 是傳指標的指標 &str(char **), 相信整死 c 初學者了。
還沒完, 如果格式轉換出錯時怎麼辦, %d 結果輸入了 abc 呢? 這個真的整死我, 花了不少時間才搞定, 因為轉換失敗, 這時候 abc 還存在 stdin buffer 裡頭, 如果使用 list 1. L12 用 while 去抓, 在我的平台會一直無窮迴圈, 因為 abc 一直存在, scanf 會一直格式錯誤, return 0, errno 會得到 EILSEQ。
解法很直覺, 清掉 stdin buffer 就好了吧, 出動 fflush(stdin), 結果沒用, 再補上 clearerr(stdin), 也沒用, 靠, stdin buffer 清不掉阿, fflush 名過其實。後來找到 __fpurge(stdin) 終於清掉 stdin buffer。這時候 scanf 就又會等在那邊等著使用者按下鍵盤輸入了。
linux man page 提到: For input streams associated with seekable files (e.g., disk files, but not pipes or terminals), 也許 stdin 不算是 seekable files 吧! 所以 fflush(stdin) 無效。另外「C程式語言」B-4 提到 fflush 對於 input streams, 結果未定義 (undefined behavior)。
__fpurge(stdin) 不確定其他平台有沒有, 請自行查詢。另外一個有可攜性的作法就是把 stdin buffer 讀出來丟掉。
int c; while ((c = getchar()) != '\n' && c != EOF);
我本來只想寫第一段那個而已, 沒想到後來補了這麼多, scanf 難阿!
ref:
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。