C 語言局部變量未初始化即使用屬於未定義行為,但「如何判斷變量是否已被賦值」本身是個偽命題——**C 無運行時機制追蹤初始化狀態**。本文剖析原文作者用 sprintf+strcmp 的誤區,給出工業級的正確模式:初始化、哨兵值、可選類型封裝、靜態分析工具。
char temp[20] = {0};
sprintf(temp, "%d", auto_value_int);
if (strcmp(temp, "") != 0) // 錯誤:sprintf 總會寫入字符,即使值為 0 也寫入 "0"
printf(...);
致命缺陷:sprintf 只要執行,**緩衝區永不為空**(至少含 '\0' 或數字字符),strcmp(temp, "") 恆為真,完全失去檢測作用。且 sprintf 無緩衝區長度檢查,存在溢出風險。
| 模式 | 代碼示例 | 適用場景 | 優/缺點 |
|---|---|---|---|
| 定義時初始化(首選) | int val = 0; / long val = -1; | 絕大多數情況 | 零成本、語義清晰、編譯器可優化 |
| 哨兵值 | int val = INT_MIN; 表示「未設置」 | 業務範圍不含哨兵值時 | 需文檔化約定,易被誤用 |
| 布爾標誌位 | bool has_val = false; int val; ... has_val=true; | 合法值覆蓋全類型範圍 | 額外變量,需原子操作保證線程安全 |
| 指針/可選封裝 | int *p = NULL; 或 C++ std::optional | 需區分「無值」與「值為 0」 | 指針增加間接訪問開銷 |
| 靜態分析/編譯器警告 | -Wuninitialized -Wmaybe-uninitialized | 編譯期捕獲 | 無法覆蓋動態路徑,需配合運行時斷言 |
assert(has_val) 或 if (!has_val) abort();。snprintf 替代 sprintf:防緩衝區溢出。-ftrivial-auto-var-init=pattern (GCC 12+/Clang) 自動用可識別模式(如 0xAA)填充未初始化自動變量,運行時立即觸發可見錯誤。**不要試圖在運行時「檢測」是否賦值**——這違背 C 語言設計哲學。應在**編寫代碼時**通過初始化、類型系統、靜態分析**保證**已賦值。將「未初始化」視為 Bug 並在編譯/靜態分析階段消滅,才是正道。