變量初始化看似簡單,實則暗藏存儲類別、作用域、生命週期、數組維度等多重規則。本文系統梳理**外部/靜態 vs 自動/寄存器**初始化語義差異、單個/數組/多維/指針數組初始化語法規則,並給出常見誤區修正表。
| 存儲類別 | 初始化時機 | 無顯式初始化時默認值 | 初始化表達式限制 |
|---|---|---|---|
| 外部 (external) | 程序啟動前 (載入階段) | 0 (全零位模式) | **常量表達式** (編譯期可計算) |
| 靜態全局 (static 外部) | 程序啟動前 | 0 | 常量表達式 |
| 靜態局部 (static 內部) | **首次進入** 定義所在函數/複合語句時 | 0 | 常量表達式 |
| 自動 (auto 局部) | **每次進入** 定義所在函數/塊時 | **不確定 (垃圾值)** | **任意表達式** (可含變量、函數調用) |
| 寄存器 (register) | 每次進入函數時 | 不確定 | 任意表達式 |
float f0, f1 = 0.2; /* 外部:f0=0.0, f1=0.2 */
main() {
static int i = 10, j; /* 靜態局部:i=10, j=0 (僅首次執行賦值) */
int k = i * 5; /* 自動局部:每次進入 main 計算 i*5=50 */
char c = 'y'; /* 字符初始化 */
/* ... */
}
關鍵:static 局部變量**初始化只執行一次** (程序啟動或首次進入),後續調用保持上次值;auto 每次進入重新初始化。
{ } 包裹,元素間 , 分隔,整體外層再加 { },以 ; 結束。int p[2][3] = {{2, -9, 0}, {8, 2, -5}};
/* 記憶體順序:p[0][0], p[0][1], p[0][2], p[1][0], p[1][1], p[1][2] */
int x[2][3] = {1, 2, 3, 4, 5, 6}; /* 等價於上述分組寫法 */
int a[5] = {1, 2}; /* 等價 {1,2,0,0,0} */
int b[2][2] = {{1}, {2}}; /* 等價 {{1,0},{2,0}} */
char *f[] = {'a', 'b', 'c'};
/* 編譯器推導長度 3,等價於:
f[0]="a", f[1]="b", f[2]="c" (實際為字符串字面量地址) */
/* 更實用的字符串指針數組 */
char *msg[] = {"Error", "Warning", "Info"};
/* msg[0] 指向 "Error" 首字符地址,以此類推 */
| 寫法 | 類型 | 大小 | 可修改性 | 存儲區 |
|---|---|---|---|---|
char s[] = "abc"; | char[4] | 4 (含 \0) | **可修改** | 棧/數據段 |
char *p = "abc"; | char* | 指針大小 | **不可修改** (指向常量區) | 只讀數據段 |
| 誤區代碼 | 問題 | 修正 |
|---|---|---|
int a[3] = {1,2,3,4}; | 初始化項超過數組長度 | 增加長度或減少項 |
int a[] = {1,2}; a[2]=3; | 長度固定為 2,越界寫入 | 預留足夠長度 int a[5]={1,2}; |
static int *p = malloc(4); | 靜態變量初始化器必須為常量表達式 | 運行時賦值:static int *p; p=malloc(4); |
char s[4] = "abcd"; | 缺少結束 \0,實際需 5 字節 | char s[5] = "abcd"; 或 char s[] = "abcd"; |
int *p = {1,2,3}; | 指針無法用列表初始化 | 分配記憶體後逐元素賦值 |
char s[] = "..."。const char* 更安全。初始化 = **存儲類別 + 作用域 + 語法規則** 的三重奏。外部/靜態「編譯期常量、零默認、一次初始化」;自動/寄存器「運行期任意表達式、垃圾默認、每次初始化」。數組按行優先展開、不足補零、指針數組退化為字符串指針陣列。精通此節,消滅「未初始化變量」這類最隱蔽、最難復現的經典 Bug。