標準 I/O 串流(stdio stream)
/ stdio -> STANDARD-eye-oh /
如字面所示去想像串流:一股朝單一方向流動的位元組,你舀取或注入它,而不必操心每個位元組究竟落在哪裡。在 C 標準函式庫中,串流是包在檔案外、那層親切又帶緩衝的外殼——就是 fopen() 交給你、printf() 寫進去的那個東西。
具體來說,stdio 串流由一個 FILE 物件代表,而你持有一個指向它的指標(一個 FILE *)。它把以下這些綁在一起:底層的檔案描述符、一塊記憶體中的緩衝區、目前的讀寫位置,以及表示檔案結尾與錯誤的狀態旗標。fopen(path, mode) 開啟一個串流(mode 是像 "r"、"w"、"a"、"rb"、"r+" 的字串);fclose() 關閉它(會先把任何待寫的位元組清出)。你透過串流函式來讀寫——fgetc()、fgets()、fread()、fputc()、fputs()、fwrite()、fprintf()、fscanf()——緩衝由函式庫替你處理。有三個串流會在 main() 執行前自動開好、永遠不需要 fopen():stdin(標準輸入串流,通常是鍵盤)、stdout(標準輸出,通常是螢幕)、stderr(標準錯誤,也是螢幕,但採無緩衝或行緩衝,好讓錯誤訊息即時出現)。它們分別坐落在檔案描述符 0、1、2 上。
為何重要:串流是幾乎所有尋常 C 程式做 I/O 的方式,因為它們可攜、帶緩衝、又好用——printf 就是個串流函式。陷阱在於分層:串流有自己的緩衝區,與核心分開,所以理解串流就意味著理解資料究竟何時離開緩衝區(見「緩衝模式與 fflush」)。而且因為串流是比原始描述符更高階的物件,你不該隨意把 FILE * 上的呼叫與其底層 fd 上的呼叫混用。
FILE *f = fopen("log.txt", "a"); /* 開啟一個附加用的串流 */ if (!f) { perror("fopen"); return 1; } fprintf(f, "started at %d\n", t); /* 透過串流的緩衝寫入 */ fclose(f); /* 清出緩衝 + 關閉 */
fopen 回傳一個 FILE *;printf 式的寫入經過它的緩衝區;fclose 清出緩衝並關閉。
fopen 失敗時回傳 NULL(不像 open 回傳 -1),而 printf 寫入的串流可能帶緩衝——輸出可能在換行、flush 或程式結束前都看不見。送往終端機的 stdout 採行緩衝;送往管線或檔案的 stdout 通常採全緩衝。