斷言是一種**調試輔助機制**,在程式運行時檢查「必須為真」的條件。若條件不成立,程式終止並輸出錯誤訊息(檔案名、行號、條件表達式),幫助開發者快速定位邏輯錯誤。
#include
void func(int *ptr) {
assert(ptr != NULL); // 斷言指標非空
// ...
}
NDEBUG,assert 生效。NDEBUG(如 -DNDEBUG 或 #define NDEBUG),assert 被預處理器移除,**零開銷**。#ifndef NDEBUG
assert(condition);
#endif
| 適用 ✅ | 不適用 ❌ |
|---|---|
| 檢查內部不變式(如陣列下標越界、指標非空) | 檢查用戶輸入、外部資源(檔案、網路)錯誤 |
| 驗證函數前置/後置條件 | 伺服器、嵌入式等**不可中斷**的生產環境 |
| 捕捉「不可能發生」的程式邏輯錯誤 | 需優雅處理的可預期錯誤(應用錯誤碼/異常) |
核心原則:斷言用於發現**程式 bug**,不用於處理**運行時錯誤**。
static_assert(constant-expression, string-literal);
// 確保結構體大小符合協議要求
static_assert(sizeof(ProtocolHeader) == 24, "ProtocolHeader size mismatch");
// 確保枚舉值連續
static_assert(ERROR_MAX - ERROR_MIN + 1 == ERROR_COUNT, "Error code gap detected");
// 確保類型寬度
static_assert(sizeof(int) >= 4, "int must be at least 32-bit");
利用「陣列大小為負則編譯錯誤」特性:
#define STATIC_ASSERT(cond, msg) \
typedef char static_assert_failed_##msg[(cond) ? 1 : -1]
// 使用範例
STATIC_ASSERT(sizeof(int) == 4, int_must_be_32bit);
文章提供的巨集技巧:
#define JOIN(X, Y) JOIN_AGAIN(X, Y)
#define JOIN_AGAIN(X, Y) X##Y
#define STATIC_ASSERT(e) \
typedef char JOIN(assert_failed_at_line_, __LINE__)[(e) ? 1 : -1]
__LINE__ 確保每次展開產生唯一類型名稱,避免重複定義錯誤。e 為 0,陣列大小為 -1 → 編譯錯誤。| 特性 | assert (運行時) | static_assert (編譯期) |
|---|---|---|
| 檢查時機 | 程式執行時 | 編譯時期 |
| 可檢查對象 | 變數、運算式、指標狀態 | 常量表達式、sizeof、類型屬性 |
| 失敗後果 | 程式中止 (abort) | 編譯失敗 |
| Release 版行為 | 自動移除 (定義 NDEBUG) | 永遠生效 |
| 適用標準 | C89 / C++98 起 | C11 / C++11 起 |
assert 保護(指標非空、索引越界、鎖狀態)。abort()。