說明
靜態程式碼檢查是現代語言的標準功能之一。Golang 內建 go vet 指令,Rust 也會在編譯時自動執行檢查。
相較之下,C 和 C++ 雖然同樣具備靜態檢查能力,但預設並不會啟用,必須手動加上參數。例如:
$ gcc -std=c11 -pedantic -Wall -Wextra -fsyntax-only program.c
這類指令本身並不複雜,但在實務上有兩個問題:
- 指令冗長,容易重複輸入
- 一旦涉及多檔案或 C / C++ 混合專案,處理方式會變得零散
進一步來看,C / C++ 編譯器本身並沒有提供多檔案管理機制。當程式規模稍微增加時,往往需要引入 Make、CMake 等 build automation 工具,才能維持可操作性。
這就形成一個常見但容易被忽略的落差:
為了做一件很單純的事情(檢查 warning),卻需要啟動一整套建構系統。
問題並不在於工具不足,而在於成本與需求之間出現了錯配。
本文介紹一個簡單的 POSIX sh 命令稿 ccwarn,用來自動化這個過程,在不引入專案設定的前提下,完成 C 與 C++ 的靜態檢查。
使用範例
$ ccwarn main.c model.c utils.c
ccwarn 會自動呼叫 GCC 與 Clang,對所有檔案進行檢查。
這樣的做法有兩個直接效果:
- 不需要撰寫 build automation 命令稿
- 可以同時檢查不同編譯器下的行為差異
由於 GCC 與 Clang 在診斷與語言擴充上存在差異,透過兩者交叉檢查,可以降低無意間依賴特定編譯器行為的風險。
在不同作業系統(例如 GNU/Linux 與 FreeBSD)下執行時,也能順便觀察是否引入平台相關特性(例如 Linux-only 行為)。
需要注意的是,ccwarn 的設計目標並不包含大型或複雜專案。對於這類情境,完整的 build automation 工具仍然是更合適的選擇。
為什麼要製作這個指令
ccwarn 並沒有引入新的語言特性,也不試圖取代既有的工具鏈。
它的定位更接近於:
在既有編譯器之上,加上一層局部的 abstraction。
這層 abstraction 並不改變語言,也不改變編譯器本身,而是重新整理「如何使用它們」的方式。
在學習 C 或 C++ 的過程中,常見的情境是:
- 撰寫短小的練習程式
- 驗證語法或語言特性
- 不打算建立長期維護的專案
在這些情況下,為每一份程式碼建立專案、設定 build automation,成本往往高於實際需求。
ccwarn 將問題重新定義為:
「這段程式碼,能否在不同編譯器下乾淨地編譯?」
當問題被壓縮到這個程度時,對應的解法也可以變得非常簡單:
- 不需要專案設定
- 不處理相依性
- 不涉及執行期行為
換句話說,它不是在增加功能,而是在移除不必要的負擔。
這樣的設計,使 ccwarn 特別適合用於:
- 小型程式碼
- 快速實驗
- warning 清理
- 可攜性檢查
相對地,在大型專案中,這些問題通常已由既有的 build system 解決,ccwarn 也就不再是主要工具。
結語
ccwarn 並不是為了取代既有工具鏈而存在。
它的價值在於示範另一種思路:
當問題本身足夠小時,未必需要引入更大的工具或改變整個環境;有時,只需要在既有基礎之上,加上一層薄薄的 abstraction,就足以改善開發體驗。
這樣的做法,或許不顯眼,但在實務中往往更直接,也更具成本效益。