| 維度 | 初始化 (定義時) | 賦值 (定義後) |
| 語法位置 | 聲明語句中 | 可執行語句中 |
| 語法形式 | type var = value; | var = value; |
| 多變量連續 | 禁止 int a = b = c = 5; | 允許 a = b = c = 5; |
| 分隔符 | 變量間用 , | 語句間用 ; |
| 執行時機 | 編譯期(靜態) / 進入作用域時(自動) | 程式運行到該行 |
| 未初始化值 | 靜態→0,自動→不定(垃圾值) | 不定(垃圾值) |
標量初始化語法
基本形式
- int a = 3;
- int b, c = 5; // b 無初值,c=5
- float x = 3.2, y = 3.0, z = 0.75;
- char ch1 = 'K', ch2 = 'P';
- double price = 123.123;
關鍵規則
- 每個變量單獨初始化:
int a = 3, b = 3, c = 3; ✅
- 禁止鏈式:
int a = b = c = 5; ❌ 編譯錯誤
- 可用前面變量初始化後面:
int a = 5, b = a, c = a + b; ✅
- 混合聲明初始化:
int a = 3, b, c = 5; ✅ (b 無初值)
聚合類型初始化:數組、結構體、聯合
數組初始化
int arr[5] = {1, 2, 3, 4, 5}; // 完全初始化
int arr[5] = {1, 2}; // 部分初始化:剩餘自動為 0 → {1,2,0,0,0}
int arr[] = {1, 2, 3}; // 省略長度:由初始化項推導 → 長度 3
char str[] = "Hello"; // 字符串字面量初始化 → {'H','e','l','l','o','\0'}
結構體初始化
struct Point { int x, y; };
struct Point p1 = {10, 20}; // 順序初始化
struct Point p2 = {.x = 10, .y = 20}; // C99 指定初始化器 (推薦)
struct Point p3 = {.y = 20}; // 部分初始化:x 自動為 0
聯合初始化
union Data { int i; float f; };
union Data d1 = {10}; // 初始化第一個成員 (int i)
union Data d2 = {.f = 3.14f}; // C99 指定初始化器:初始化指定成員
嵌套聚合初始化
struct Rectangle {
struct Point tl, br;
};
struct Rectangle r = {
{0, 0}, // tl
{100, 100} // br
};
// C99 指定初始化器更清晰
struct Rectangle r2 = {
.tl = {.x = 0, .y = 0},
.br = {.x = 100, .y = 100}
};
靜態 vs 自動存儲期:初始化語義差異
| 特性 | 靜態存儲期 (全局/static) | 自動存儲期 (局部非static) |
| 未初始化默認值 | 零初始化 (所有位為 0) | 不定值 (垃圾值,堆疊殘留) |
| 初始化表達式 | 必須為常量表達式 (編譯期可計算) | 可為任意運行時表達式 |
| 初始化次數 | 程式生命週期一次 (載入時) | 每次進入作用域執行一次 |
| 零初始化語義 | 指針→NULL,浮點→0.0,整數→0 | 無自動零初始化 |
靜態初始化範例
int global = 10; // 常量表達式 ✅
int global2 = func(); // ❌ func() 非常量表達式 (C 標準禁止,GCC 擴展允許)
static int count = 0; // 靜態局部變量,零初始化後再賦 0 (優化合併)
自動初始化範例
void func(int n) {
int a = n * 2; // 運行時表達式 ✅
int b = rand(); // 運行時表達式 ✅
// int c; // 危險!未初始化,值不定
}
C99 指定初始化器:現代最佳實踐
語法:.成員名 = 值 (結構體/聯合) 或 [索引] = 值 (數組)
優勢
- 順序無關:
{.y=20, .x=10} 等價 {.x=10, .y=20}
- 可讀性強:字面標註哪個成員被初始化
- 安全擴展:結構體新增成員不影響現有初始化代碼
- 部分初始化明確:未指定成員自動零初始化
數組指定初始化
int arr[10] = {[0] = 1, [5] = 5, [9] = 9}; // 特定索引初始化
int arr[] = {[2] = 10, [5] = 20}; // 長度自動推導為 6 (最大索引+1)
int matrix[2][3] = {[0][1] = 5, [1][2] = 10}; // 多維數組
經典範例 3-11
- int main(){
- int a = 3, b, c = 5;
- b = a + c;
- printf("a=%d,b=%d,c=%d\n", a, b, c);
- }
輸出:a=3,b=8,c=5
⚠️ 經典陷阱與避坑指南
🚫 陷阱 1:鏈式初始化語法錯誤
int a = b = c = 5; // ❌ 編譯錯誤:初始化器不允許賦值表達式
// 正確
int a = 5, b = 5, c = 5;
🚫 陷阱 2:局部變量未初始化使用
void func() {
int x;
if (x > 0) ... // ❌ 未定義行為!x 為垃圾值
}
// 正確:定義即初始化
void func() {
int x = 0; // 或從輸入/計算獲得初值
}
🚫 陷阱 3:靜態變量非常量初始化
int global = get_value(); // ❌ 標準 C 禁止:全局/靜態初始化器必須為常量表達式
// 正確:延遲初始化模式
int global;
bool initialized = false;
void init_global() {
if (!initialized) { global = get_value(); initialized = true; }
}
🚫 陷阱 4:字符數組字符串初始化長度陷阱
char str[5] = "Hello"; // ❌ "Hello" 含結束符 \0 需 6 字節,溢出!
// 正確
char str[] = "Hello"; // 自動推導長度 6
char str[6] = "Hello"; // 顯式足夠長度
char str[5] = {'H','e','l','l','o'}; // 無結束符,長度 5 (非字符串)
🚫 陷阱 5:指針初始化為整數常量
int *p = 0; // ✅ 0 是空指針常量
int *p = NULL; // ✅ 標準宏
int *p = (int*)0; // ✅ 顯式轉換
int *p = 1234; // ❌ 整數轉指針需強制轉換,且地址通常無效
最佳實踐清單
- 定義即初始化:
int x = get_initial(); 杜絕未初始化變量
- 結構體/數組用指定初始化器 (C99):
{.field = val}, [idx] = val
- 字符串初始化用
char str[] = "...":自動計算含結束符長度
- 靜態變量利用零初始化:
static int count; 自動為 0,無需顯式 =0
- 指針初始化為
NULL 或 0:int *p = NULL;
- 避免全局變量:傾向局部靜態或傳遞參數
- 啟用編譯器警告:
-Wuninitialized -Wmaybe-uninitialized
總結
C 語言初始化機制涵蓋:標量單獨初始化、聚合類型大括號初始化、C99 指定初始化器、靜態存儲期零初始化、常量表達式約束。核心原則:
- 初始化 ≠ 賦值:語法、時機、規則全不同
- 禁止鏈式初始化:每個變量獨立指定初值
- 自動變量必須顯式初始化:無默認零值
- 指定初始化器是現代 C 的最佳實踐:安全、可讀、可維護
養成「聲明即初始化」習慣,善用 C99 指定初始化器,是消除未初始化變量 Bug、提升代碼質量的最高性價比手段。
- 上一篇文章: C語言數據類型轉換:自動轉換規則與強制轉換完整指南
- 下一篇文章: C語言字符類型完整指南:字符常量、轉義字符、字符變量、內存存儲與字符串
Copyright ©
程式碼之家 All Rights Reserved