{0} 是 C/C++ 中最常見卻最易被誤解的初始化寫法。本文依據 C99 標準(ISO/IEC 9899:1999),徹底釐清其語義、遞歸規則與最佳實踐。
6.7.8.21:初始化列表項目少於聚合體成員時,剩餘成員按靜態存儲期對象規則隱式初始化(即零初始化)。
6.7.8.20:聚合體包含子聚合體/聯合體時,規則遞歸應用。若子聚合體初始化以
{開始,用其內部列表初始化;否則從列表取「足夠」項目初始化該子聚合體,剩餘項目留給下一成員。
struct { int a, b; } s = {0};
// 等價於 {0, 0}
// a 從列表獲取 0,b 隱式零初始化
struct Inner { int x, y; };
struct Outer { Inner in; int z; };
Outer o1 = {1, 2}; // 非大括號開啟 → 取足夠項目給 in
// in.x=1, in.y=2, z=0 (隱式)
Outer o2 = {{1, 2}}; // 大括號開啟 → 列表給 in
// in.x=1, in.y=2, z=0
Outer o3 = {{1}, 2}; // in 列表只有 1 項 → in.x=1, in.y=0, z=2
| 類型 | {0} 效果 |
|---|---|
| 標量 | 零初始化 |
| 結構體 | 所有成員遞歸零初始化 |
| 數組 | 所有元素遞歸零初始化 |
| 聯合體 | 第一個成員零初始化 |
| 嵌套混合 | 全遞歸零初始化 |
memset 或向量化存儲指令{} 列表初始化,{0} 仍有效但 {} 更推薦{.a=0},但 {0} 仍通用{0}
// 推薦:結構體變量定義時清零
struct sockaddr_in addr = {0};
// 推薦:數組清零
int buf[1024] = {0};
// 避免:malloc 後用 {0} (只能用 memset)
// 避免:非聚合體類類型
// 避免:指針類型({0} 將指針置空,但語義不如 nullptr/NULL 明確)
// 陷阱 1:指針數組
int *ptrs[10] = {0}; // 所有指針置 NULL ✓
// 陷阱 2:結構體含指針
struct { int *p; } s = {0}; // p = NULL ✓
// 陷阱 3:C++ 非聚合體
class Foo { int x; public: Foo() {} }; // 有用戶聲明構造函數
Foo f = {0}; // 編譯錯誤!非聚合體
// 陷阱 4:聯合體首個成員非標量
union { struct { int a; } s; int b; } u = {0};
// 初始化 s.a = 0,b 未初始化(但聯合體共享內存,實際全 0)
| 方式 | 適用範圍 | 類型安全 | 性能 | 備註 |
|---|---|---|---|---|
| {0} | 聚合體定義時 | 高 | 極佳 | 編譯期常數,編譯器優化最好 |
| memset | 任意內存塊 | 無 | 好 | 運行期,易出錯(非 POD 類型 UB) |
| 默認初始化 | 靜態/線程局部變量 | 高 | 無開銷 | 自動零初始化,但局部變量不保證 |
value initialization () | C++ 類類型 | 高 | 好 | T t = T(); 或 new T() |
{0} 是 C 語言中最優雅、標準保證最強的聚合體零初始化寫法。理解其背後的遞歸初始化規則,不僅能寫出正確代碼,還能在代碼審查中自信駁回「建議用 memset」的無效建議。C++ 開發者在聚合體場景下同樣適用,現代 C++ 更推薦 {} 以避免窄化轉換風險。