初始化:宣告時賦予起始值
初始化規則取決於儲存期 (靜態 vs 自動) 與型別 (標量、陣列、指標)。
儲存期決定初始化時機與預設值
| 類別 | 範例 | 初始化時機 | 無初始化器時預設值 | 初始化器型別限制 |
| 外部/靜態全域 | static int g; | 程式載入前 (編譯期可知) | 0 (整數)、0.0 (浮點)、NULL (指標) | 必須為常數表達式 |
| 靜態區域 | static int f(void){static int s;} | 程式載入前 (同外部) | 同上 | 必須為常數表達式 |
| 自動區域 | void f(void){int a;} | 每次進入區塊執行時 | 不定值 (垃圾值) | 可為任意運算式 (含函數呼叫) |
| register | register int r; | 同自動 | 不定值 | 可為任意運算式 |
標量初始化範例
float f0, f1 = 0.2; /* f0=0.0 (外部靜態), f1=0.2 */
int main(void)
{
static int i = 10, j; /* i=10, j=0 (靜態區域) */
int k = i * 5; /* k=50 (自動,運算式含變數) */
char c = 'y'; /* 字元常數 */
/* ... */
}
陣列初始化規則 (重點)
- 巢狀大括號:每維一組
{},最外層再包一層,逗號分隔,分號結束。
- 最右維變化最快:記憶體順序與初始化列表順序一致。
- 扁平化合法:
int x[2][3] = {1,2,3,4,5,6}; 等同 {{1,2,3},{4,5,6}}。
- 提供者少於元素數:剩餘元素自動補 0 (整數/浮點) 或
NULL (指標)。
- 指標陣列可省略長度:
char *f[] = {"A","B","C"}; 推斷為 3 元素,f[0] 指向 "A" 等。
陣列初始化範例
int main(void)
{
int p[2][3] = { {2, -9, 0}, {8, 2, -5} };
int m[2][4] = { {27, -5, 19, 3}, {1, 8, -14, -2} };
char *f[] = { 'A', 'B', 'C' }; /* 3 個 char* */
int flat[2][3] = { 1, 2, 3, 4, 5, 6 }; /* 扁平化 */
int partial[3][2] = { {1}, {2, 3} }; /* 餘額補 0 */
/* partial = {{1,0}, {2,3}, {0,0}} */
}
指標變數初始化
int main(void)
{
int *pi = (int *)0x1234; /* 直接指定位址 (嵌入式常見) */
float *pf = &some_float; /* 取現有物件位址 */
char *ps = "Good"; /* 指向字串常值區 (唯讀) */
/* 注意:字串常值不可修改,若需修改請用陣列 */
char arr[] = "Good"; /* 可修改的副本 */
}
關鍵區別:字串指標 vs 字元陣列初始化
| 宣告 | 儲存位置 | 可修改 | sizeof |
char *p = "Hi"; | 唯讀資料段 (.rodata) | 否 (UB) | 指標大小 (4/8) |
char a[] = "Hi"; | 堆疊/資料段 | 是 | 3 (含 \0) |
最佳實務清單
- 總是初始化:消除「未初始化變數」這類最隱蔽的 Bug 來源。
- 靜態儲存期用常數表達式:編譯器可檢查並優化。
- 陣列初始化偏好扁平化或巢狀:視可讀性選擇,扁平化便於巨集產生。
- 字串常值指標加
const:const char *p = "Hi"; 讓編譯器攔截誤寫。
- 結構體指定初始化器 (C99):
struct S {int a,b;} s = {.b=5}; 明確、抗重構。