接續《編寫高質量C代碼:斷言基礎》,本文深入探討斷言在**防錯性編程**、**算法交叉驗證**、**初始檢查**三大進階場景的應用,通過字符解壓程式演進展示如何「不隱瞞錯誤、盡早發現錯誤、多算法互驗」。
byte* expand(byte *src, byte *dst, size_t len) {
byte *end = src + len;
while(src < end) { // 防錯:src 不會超過 end
byte b = *src++;
if(b == REPEAT_CODE) {
byte val = *src++;
byte cnt = *src++;
while(cnt--) *dst++ = val;
} else {
*dst++ = b;
}
}
return dst; // 即使 src > end 也會安靜返回
}
問題:輸入損壞導致 src > end 時,迴圈直接退出,**錯誤被隱瞞**,調用方以為成功。
while(src != end) { // 嚴格相等判斷
// ...
do *dst++ = val; while(--cnt != 0); // cnt=0 會多執行一次
}
問題:src > end 導致無限迴圈;cnt=0 導致越界寫入。**崩潰雖暴露錯誤,但損害用戶數據**。
byte* expand(byte *src, byte *dst, size_t len) {
byte *end = src + len;
while(src < end) { // 防錯:保護性邊界
byte b = *src++;
if(b == REPEAT_CODE) {
byte val = *src++;
byte cnt = *src++;
// 算法邏輯驗證:cnt>=4 或 (cnt==1 且 val==REPEAT_CODE)
assert(cnt >= 4 || (cnt == 1 && val == REPEAT_CODE));
while(cnt--) *dst++ = val;
} else {
*dst++ = b;
}
}
// 終止條件驗證:必須精確消耗所有輸入
assert(src == end);
return dst;
}
「在進行防錯性編程時,若『不可能發生』的情況確實發生,必須用斷言報警。」
「利用不同算法對程序結果進行確認,當同一問題的不同算法出現不同結果時,觸發斷言。」
// 排序驗證
void sort_verify(int *a, int n) {
int *b = malloc(n * sizeof(int));
memcpy(b, a, n * sizeof(int));
quicksort(a, n);
mergesort(b, n); // 完全不同算法
for(int i = 0; i < n; i++)
assert(a[i] == b[i]); // 結果必須一致
free(b);
}
// 校驗和驗證
uint32_t crc32(const byte *data, size_t len);
uint32_t adler32(const byte *data, size_t len);
assert(crc32(data, len) == expected_crc);
assert(adler32(data, len) == expected_adler); // 雙重校驗
**適用範圍**:關鍵演算法 (排序、壓縮、加密、數值積分)、序列化/反序列化、分散式一致性檢查。
「不要等待錯誤發生,要使用初始檢查程序,盡早發現錯誤。」
// 檔案處理前檢查
FILE* safe_open(const char *path, const char *mode) {
assert(path != NULL);
assert(mode != NULL);
assert(strlen(path) < MAX_PATH);
FILE *fp = fopen(path, mode);
assert(fp != NULL); // 立即失敗,而非後續 NULL 解引用
return fp;
}
// 記憶體分配檢查
void* safe_malloc(size_t size) {
assert(size > 0 && size < MAX_ALLOC);
void *p = malloc(size);
assert(p != NULL); // OOM 立即終止,避免後續邏輯混亂
return p;
}
| 級別 | 宏 | 啟用條件 | 用途 |
|---|---|---|---|
| 核心 | ASSERT | Debug + Release | 安全關鍵:空指標、緩衝區邊界、協議魔數 |
| 邏輯 | DEBUG_ASSERT | Debug only | 演算法不變量、迴圈變體、資源配對 |
| 契約 | CONTRACT_ASSERT | Debug + 測試 | 前置/後置條件、介面契約 |