早期 C(K&R C)允許函數定義分離參數類型聲明:
int f(c) char c; { return c; } // K&R 風格
ANSI C (C89) 引入原型聲明,要求參數列表包含類型:
int f(char c); // 原型聲明
int f(char c) { return c; } // 定義
現代編譯器為兼容舊代碼仍支持 K&R 定義,但嚴禁在新代碼中使用。
int ff(); // 舊式聲明:參數類型未指定
// 僅允許在調用時傳遞 int、double 等經過「默認參數提升」的類型
// char/short 會提升為 int,float 提升為 double
若實際定義包含 float/char 參數,調用時傳遞的提升類型與定義不符,導致棧幀解析錯誤、未定義行為。鐵律:所有函數必須提供完整原型聲明(C99 強制)。
char/short → intfloat → doubleprintf("%d", c) 能正確打印 char 的原因(實際傳遞的是 int)。...)的固定參數也遵循此規則。// 無前置聲明直接調用
float max(float x, float y); // 忘記聲明
// 編譯器假隱式聲明為 int max();
// 返回值被截斷為 int,調用約定錯誤 -> 災難性後果
C99 移除隱式函數聲明,現代編譯器默認報錯。最佳實踐:頭文件中聲明所有外部函數,源文件包含自身頭文件自檢。
C 語言只有值傳遞。傳遞指針時,傳遞的是地址值的副本。函數內修改指針指向的內容可影響外部,但修改指針變量本身(如 p = NULL)不影響實參。
-Wstrict-prototypes -Wmissing-prototypes -Wold-style-definition。clang-tidy 檢查 modernize-use-nullptr、bugprone-unused-return-value 等。理解歷史遺留特性是為了不再使用它們。現代 C 開發應建立在 C99/C11 標準之上,配合編譯器診斷與靜態分析,將類型安全落實到每一次函數調用中。