首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言中fgetc/fputc與getc/putc的深層區別

     編輯:關於C語言

C語言中fgetc/fputc與getc/putc的深層區別

許多初學者以為fgetc、fputc中的f代表file,專門用於文件操作;而getc、putc用於標準輸入輸出。但查看函數聲明會發現:四個函數都接受FILE*參數,都能操作文件流。真相是什麼?

核心區別:實現機制不同

  • fgetc/fputc:函數實現
  • getc/putc:宏實現(通常定義為宏展開)

這一區別源自《UNIX環境高級編程》(APUE)的說明:f代表function,而非file。

三大實際差異

1. 宏參數副作用陷阱

宏直接文本替換,參數若含副作用會導致多次求值:

#define MAX(a,b) ((a) > (b) ? (a) : (b))
int x = 5, y = 3;
int z = MAX(x++, y++);  // 展開後:((x++) > (y++) ? (x++) : (y++))
// x或y會被遞增兩次!

規則:getc/putc的參數不能包含副作用表達式(如getc(fp++)),而fgetc無此限制。

2. 函數地址可傳遞性

函數有地址,可作為函數指針參數傳遞;宏無地址:

int (*func_ptr)(FILE*) = fgetc;  // 合法
// int (*func_ptr)(FILE*) = getc; // 非法:宏不是函數

這在需要回調函數的場景(如qsort比較函數)至關重要。

3. 調用性能差異

  • 函數調用:需壓棧(保存返回地址、寄存器)、跳轉、執行、彈棧、返回——開銷較大
  • 宏調用:預處理階段直接展開為內聯代碼,無函數調用開銷

因此getc通常比fgetc快一點。但現代編譯器優化(內聯、LTO)常能消除這一差距。APUE用「很可能」措辭正是此意。

GNU C庫的特殊實現

在glibc中,getc和fgetc實際上都是函數!stdio.h中:

#define getc(_fp) _IO_getc(_fp)  // 宏包裝函數調用

_IO_getc在libio.h中聲明為函數。這意味著:

  • GNU C下兩者性能幾乎相同
  • 副作用陷阱依然存在(因為是宏展開)
  • 無法取地址問題依然存在

最佳實踐建議

  1. 一般情況:隨意使用getc/putc,性能略優且代碼簡潔
  2. 參數有副作用:必須用fgetc/fputc
  3. 需傳遞函數指針:必須用fgetc/fputc
  4. 跨平台代碼:優先用fgetc/fputc,避免宏實現差異

總結

f前綴真正含義是function,區別在於函數vs宏的實現機制。理解這一點,能避免副作用Bug、正確使用函數指針、做出性能優化選擇。在現代編譯器和glibc下,實際差異已很小,但語義區別依然重要。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved