C 語言缺乏異常機制,錯誤處理是代碼質量分水嶺。本文對比三種經典模式,分析優劣,並給出生產級推薦方案:錯誤碼返回 + RAII 風格清理 + 統一出口。
int foo(int bar) {
int ret = 0, ok = 1;
ok = ok && do_something(bar);
ok = ok && init_stuff();
ok = ok && prepare_stuff();
if (ok) ret = do_the_thing(bar);
return ret;
}
int foo(int bar) {
if (!do_something(bar)) goto error;
if (!init_stuff()) goto error;
if (!prepare_stuff()) goto error;
return do_the_thing(bar);
error:
// 統一清理
return 0;
}
int foo(int bar) {
int ret = 0;
if (do_something(bar)) {
if (init_stuff()) {
if (prepare_stuff()) {
ret = do_the_thing(bar);
}
}
}
return ret;
}
int foo(int bar) {
int ret = -EINVAL;
Resource *r1 = NULL, *r2 = NULL;
do {
if (!do_something(bar)) break;
r1 = alloc_resource1(); if (!r1) { ret = -ENOMEM; break; }
r2 = alloc_resource2(); if (!r2) { ret = -ENOMEM; break; }
if (!prepare_stuff(r1, r2)) { ret = -EIO; break; }
ret = do_the_thing(bar);
} while (0);
// 反序清理 (安全釋放 NULL)
free_resource2(r2);
free_resource1(r1);
return ret;
}
do { ... } while(0) 構造可 break 的塊作用域NULL 參數 (免判空)// 通用清理句柄
typedef struct { void *ptr; void (*cleanup)(void*); } CleanupHandle;
#define CLEANUP_VAR(name, ptr, fn) CleanupHandle name = {ptr, fn}
#define CLEANUP_FREE(h) do { if ((h).cleanup) (h).cleanup((h).ptr); } while(0)
int foo(int bar) {
int ret = -EINVAL;
void *r1 = alloc_resource1(); CLEANUP_VAR(h1, r1, free_resource1);
void *r2 = alloc_resource2(); CLEANUP_VAR(h2, r2, free_resource2);
if (!do_something(bar)) goto out;
if (!prepare_stuff(r1, r2)) { ret = -EIO; goto out; }
ret = do_the_thing(bar);
out:
CLEANUP_FREE(h2);
CLEANUP_FREE(h1);
return ret;
}
void free_resource1_wrapper(void *p) { free_resource1(*(void**)p); }
void free_resource2_wrapper(void *p) { free_resource2(*(void**)p); }
int foo(int bar) {
int ret = -EINVAL;
void *r1 __attribute__((cleanup(free_resource1_wrapper))) = alloc_resource1();
void *r2 __attribute__((cleanup(free_resource2_wrapper))) = alloc_resource2();
if (!do_something(bar)) return ret;
if (!prepare_stuff(r1, r2)) return -EIO;
ret = do_the_thing(bar);
return ret; // 自動按逆序調用 cleanup 函數
}
| 維度 | 標誌變量 | goto error | 嵌套 if | do-while 清理 | cleanup 屬性 |
|---|---|---|---|---|---|
| 可讀性 | 中 | 高 | 低 | 高 | 極高 |
| 資源安全 | 差 | 好 | 差 | 好 | 最佳 |
| 擴展性 | 中 | 中 | 差 | 好 | 最佳 |
| 標準合規 | C89 | C89 | C89 | C89 | GNU/Clang/MSVC 擴展 |
| 推薦等級 | ⭐ | ⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ (條件允許) |
ERR_* / -E*,禁用魔術數字C 語言錯誤處理的本質是**確定性資源管理**。推薦:GCC/Clang 環境首選 `cleanup` 屬性;跨平台項目用 `do-while(0)` + 反序清理宏;嚴禁裸指針手動管理、禁用深層嵌套、慎用 goto (除非團隊規範允許)**。將錯誤處理模式標準化納入編碼規範,配合靜態分析檢查資源洩漏,是 C 項目工程化的關鍵一步。