在維護 C 專案時,常遇到函數內部有多個 return 路徑,每條路徑前都要重複釋放資源的代碼。這不僅讓代碼顯得冗長,更埋下隱患:一旦維護者在新增返回路徑時忘記釋放,就會造成記憶體洩漏。C++ 有 RAII 機制自動管理資源,但 C 語言缺乏自動析構,我們需要一些技巧來達到類似效果。
將所有資源釋放代碼移至函數末尾,加上標籤(如 _END_F),原本的 return 改為 goto _END_F,並引入 return_code 變量保存返回值:
int f() {
int return_code = 0;
char *p1 = malloc(1024);
int *p2 = malloc(10 * sizeof(int));
// ... 業務邏輯 ...
if (error1) {
return_code = 1;
goto _END_F;
}
for (...) {
if (error2) {
return_code = 2;
goto _END_F;
}
}
// ... 正常結束 ...
_END_F:
free(p1);
free(p2);
return return_code;
}
這樣消除了重複代碼,降低遺漏釋放的風險,但每次跳轉前仍需手動賦值 return_code,仍有疏漏可能。
定義宏 RETURN(label, returnCode),一次完成返回值設置與跳轉:
#define RETURN(label, returnCode) { return_code = returnCode; goto label; }
調用點變為 RETURN(_END_F, 2),代碼更簡潔,也杜絕了忘記設置返回值的錯誤。最終重構後的函數結構清晰、資源管理集中,是 C 語言中實現「確定性清理」的經典模式。
雖然 goto 在結構化編程中常被避諱,但在 C 語言資源管理場景下,配合宏使用是公認的最佳實踐之一。它將「獲取資源—使用—釋放資源」的生命週期明確劃分,讓維護者只需關注業務邏輯,資源釋放由統一出口保證。