當一個程序生成輸出時,是否有必要將輸出立即展示給用戶?這個問題的答案根據不同的程序而定。
例如,假設一個程序輸出到終端,向終端前的用戶提問,要求用戶回答,那麼爲了讓用戶知道應該鍵入什麼內容,程序輸出應該立即地顯示給用戶。另一種情況是,假設一個程序輸出到一個文件,然後輸出到一個行式打印機,那麼只要程序結果最後都全部輸出到了目標(文件或打印機)就可以了。
程序輸出有兩種方式:一種是即時處理方式,另一種是先暫存起來,然後再大塊寫入的方式,前者往往造成較高的系統負擔。因此,C語言實現通常都允許程序員進行實際的寫操作之前控制產生的輸出數據量。
這種控制能力一般是通過庫函數setbuf實現的。如果buf是一個大小適當的字符數組,那麼
- setbuf(stdout, buf);
語句將通知輸入/輸出庫,所有寫入到stdout的輸出都應該使用buf作爲輸出緩衝區,直到buf緩衝區被填滿或者程序直接調用fflush(譯註:對於由寫操作打開的文件,調用fflush將導致輸出緩衝區的內容被實際地寫入該文件),buf緩衝區中的內容才實際寫入到stdout中。緩衝區的大小由系統頭文件<stdio.h>中的BUFSIZ定義。
下面的程序的作用是把標準輸入的內容複製到標準輸出中,演示了setbuf庫函數最顯而易見的用法:
- #include <stdio.h>
- main()
- {
- int c;
- char buf[BUFSIZ];
- setbuf(stdout, buf);
- while ((c = getchar()) != EOF)
- putchar(c);
- }
遺憾的是,這個程序是錯誤的,僅僅是因爲一個細微的原因。程序中對庫函數setbuf的調用,通知了輸入/輸出庫所有字符的標準輸出應該首先緩存在buf中。要找到問題出自何處,我們不妨思考一下buf緩衝區最後一次被清空是在什麼時候?答案是在main函數結束之後,作爲程序交回控制給操作系統之前C運行時庫所必須進行的清理工作的一部分。但是,在此之前buf字符數組已經被釋放!
要避免這種類型的錯誤有兩種辦法。第一種辦法是讓緩衝數組成爲靜態數組,即可以直接顯示聲明buf爲靜態:
- static char buf[BUFSIZ];
也可以把buf聲明完全移到main函數之外。第二種辦法是動態分配緩衝區,在程序中比不主動釋放該緩衝區,這樣C運行時庫進行清理工作時就不會發生緩衝區已釋放的情況:
- char *malloc();
- setbuf(stdout, malloc(BUFSIZ));
如果讀者關心一些編程“小技巧”,也許會注意到這裏其實並不需要檢查malloc函數調用是否成功。如果malloc函數調用失敗,將返回一個null指針。setbuf函數的第二個參數取值可以爲null,此時標準輸出不需要進行緩衝。這種情況下,程序仍然能夠工作,只不過速度較慢而已。