除錯與工具

靜態分析與程式碼檢查工具(static analysis / linter)

/ linter -> LIN-ter /

到目前為止每一個除錯工具都要執行你的程式、看它出錯。但要是有一個工具能讀你的程式碼、在從不執行它的情況下就警告你可能的臭蟲——就像一位細心的校對者光是讀就看出「你寫了 5、但意思是 5.0」或「這個分支從不檢查指標是不是 null」——會怎樣?靜態分析正是如此:藉由對原始碼(或編譯後的程式碼)推理、而非執行它,來檢查問題。程式碼檢查工具(linter)是這個想法的日常、輕量版本。

具體來說,「靜態」意味著在編譯期、不執行程式(相對於像消毒器那種觀察一次活的執行的「動態」工具)。一個靜態分析器解析你的程式碼、追蹤可能的執行路徑,尋找很可能是臭蟲的模式:一個在被設定之前就被使用的變數、一個在它可能為 null 的路徑上被解參考的指標、一個沒有對應 free 的 malloc(洩漏)、一個可能越界的陣列索引、死碼、缺漏的 return。linter 是這個的一個快速、以規則為主的子集——它標出風格問題與常見錯誤(未使用的變數、可疑的比較、被遮蔽的名稱)。連你的編譯器都是一個基礎的靜態分析器:「gcc -Wall -Wextra」啟用許多能逮到真臭蟲的警告,而像 clang-tidy、cppcheck 或 clang 靜態分析器這樣的專用工具走得更深。關鍵在於,因為它對所有路徑推理、而非只對你測試跑到的那幾條,靜態分析能找出你從未執行到的路徑上的臭蟲。

為何重要:靜態分析在程式還沒執行之前、在你測試漏掉的程式路徑上、以接近零的執行期成本逮到臭蟲——打開編譯器警告是 C 裡價值最高的習慣之一。它與消毒器的互補關係很精確:消毒器證明某個臭蟲在某次特定執行上發生了;靜態分析警告某個臭蟲在某條路徑上可能發生。這個差別帶來誠實的提醒:靜態分析器會回報假陽性(對其實不可能發生的臭蟲的警告,因為對所有路徑推理在一般情況下是不可判定的)與假陰性(它無法證明的真臭蟲),所以技巧是把警告當成待調查的線索、刻意地去修它們或抑制它們,並且永遠別以為一次乾淨的靜態分析就代表正確——它代表的是「這些規則找不到的問題」。

$ gcc -Wall -Wextra prog.c 可能印出「warning: variable y is used uninitialized in this function」或「control reaches end of non-void function」——在一次都還沒執行之前、在沒有任何測試走到的路徑上、於編譯期逮到的真臭蟲。像 cppcheck 這樣的專用工具也會找出洩漏與空指標解參考。

編譯器警告(一種基礎的靜態分析)在程式執行之前就逮到一次未初始化的使用。

靜態分析對所有路徑推理、但無法執行,所以它會產生假陽性(對不可能發生之臭蟲的警告)與假陰性(它無法證明的真臭蟲)——一份乾淨的報告不是正確性的證明。它補足、絕非取代那些動態消毒器,後者證明某個臭蟲在一次真實執行上發生了。

又称
static analyzerlinterlintcode analysis靜態分析器