第 12 章:初始化 —— 给变量一个'人生起点'

第 12 章:初始化 —— 给变量一个"人生起点"

嗨,未来的 C 语言大师!欢迎来到第 12 章!

你有没有想过,当我们声明一个变量的时候,它在内存里到底是什么状态?是一片空白?还是随机炫彩的"垃圾"数据?

恭喜你,如果你是第一次听说"垃圾值"这个词 —— 今天的课程将是你人生中最重要的"断舍离"教学!

初始化这个问题,说大不大,说小不小。但无数 C 语言老鸟都在这里翻过车,甚至有些隐藏多年的 bug,罪魁祸首就是 —— 忘了初始化

想象一下:你去买一杯奶茶,结果店员没问你想要什么,直接给你端上了一杯不知道放了多久的"神秘液体"。你喝还是不喝?

变量初始化,就是给变量一个"明确的起点"。没初始化的变量,就像那杯神秘液体 —— 你永远不知道里面是什么(可能是奶茶,也可能是邻居的洗洁精)。

好了,让我们开始这场"初始化"的神奇之旅吧! 🚀


12.1 标量初始化:最基础的"赋值出生证"

什么是标量?

标量(Scalar),听起来高大上对吧?其实就是最简单的一类数据类型——只有一个值的变量

包括:

  • 整型:intcharlong
  • 浮点型:floatdouble
  • 指针:int *char *

初始化语法:int x = 42;

这就是最经典的初始化方式——声明时直接给值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main() {
    // 标量初始化示例
    int age = 25;           // 我的年龄是 25 岁
    float height = 1.75f;   // 身高 1.75 米(注意 float 要加 f 后缀)
    double pi = 3.14159;    // 圆周率
    char grade = 'A';       // 成绩等级 A
    int *ptr = NULL;        // 指针初始化为 NULL(空指针)

    printf("年龄: %d\n", age);           // 输出: 年龄: 25
    printf("身高: %.2f\n", height);       // 输出: 身高: 1.75
    printf("圆周率: %.5f\n", pi);          // 输出: 圆周率: 3.14159
    printf("等级: %c\n", grade);          // 输出: 等级: A

    return 0;
}

为什么要初始化?

标量初始化就像给变量发一张"身份证"——上面写明了它的姓名(变量名)和出生日期(初始值)。没有身份证的变量,在 C 语言的江湖里就是"黑户"!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

int main() {
    // 已经初始化的变量
    int initialized = 100;
    printf("已初始化: %d\n", initialized);  // 输出: 已初始化: 100

    // 未初始化的局部变量(危险!)
    int uninitialized;
    printf("未初始化: %d\n", uninitialized);  // 输出: 未初始化: 一个随机垃圾值(每次可能不同!)

    return 0;
}

警告:上面代码中的 uninitialized 变量,它的值是上一次使用这块内存时留下的"遗迹"——可能是 0,可能是 42,也可能是 -858993460。这就是传说中的"未定义行为"(Undefined Behavior),C 语言标准并没有规定这时候应该是什么值!


12.2 聚合初始化:数组和结构体的"批量出道"

数组初始化

如果说标量初始化是给一个人发身份证,那聚合初始化就是给一整个家族(数组)同时办理户口本!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>

int main() {
    // 方式一:完全初始化——每个位置都指定值
    int scores[5] = {90, 85, 88, 92, 78};
    // 现在 scores 数组 = {90, 85, 88, 92, 78}

    // 方式二:部分初始化——只给前面的赋初值
    int partial[5] = {10, 20, 30};
    // 现在 partial 数组 = {10, 20, 30, 0, 0}
    // 没指定的会自动填 0!

    // 方式三:省略长度——让编译器自己数
    int auto_size[] = {1, 2, 3, 4, 5};
    // 编译器自动把长度设为 5

    // 打印验证
    printf("完全初始化: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", scores[i]);  // 输出: 90 85 88 92 78
    }
    printf("\n");

    printf("部分初始化: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", partial[i]);  // 输出: 10 20 30 0 0
    }
    printf("\n");

    printf("自动长度: %lu\n", sizeof(auto_size) / sizeof(auto_size[0]));  // 输出: 5

    return 0;
}

部分初始化的魔法

敲黑板!这是重点!

当你只给数组的部分元素初始化时,C 语言有个非常贴心的规则:剩余元素自动默认为 0

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main() {
    // 只初始化第一个元素
    int arr[10] = {42};

    printf("arr[0] = %d\n", arr[0]);  // 输出: 42
    printf("arr[1] = %d\n", arr[1]);  // 输出: 0
    printf("arr[2] = %d\n", arr[2]);  // 输出: 0
    // ... 后面所有都是 0!

    // 巧用这个特性:全部初始化为 0
    int zero[5] = {0};  // 所有元素都是 0!

    printf("全部为零: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", zero[i]);  // 输出: 0 0 0 0 0
    }
    printf("\n");

    return 0;
}

小贴士:如果你想快速创建一个全零数组,int arr[100] = {0}; 是最简洁的方式!记住,花括号里只需要写一个 0 就够了。

结构体初始化

结构体(struct)的初始化和数组类似,但可以更灵活——因为结构体有多个成员嘛!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <string.h>

// 定义一个结构体:学生信息
struct Student {
    char name[50];
    int age;
    float gpa;
};

int main() {
    // 方式一:按声明顺序初始化(经典方式)
    struct Student s1 = {"张三", 20, 3.75f};
    printf("学生1: %s, %d岁, GPA: %.2f\n", s1.name, s1.age, s1.gpa);
    // 输出: 学生1: 张三, 20岁, GPA: 3.75

    // 方式二:指定初始化器(C99,可不按顺序)
    struct Student s2 = {.gpa = 3.9f, .name = "李四", .age = 21};
    printf("学生2: %s, %d岁, GPA: %.2f\n", s2.name, s2.age, s2.gpa);
    // 输出: 学生2: 李四, 21岁, GPA: 3.90

    // 方式三:先声明,后赋值
    struct Student s3;
    strcpy(s3.name, "王五");
    s3.age = 19;
    s3.gpa = 3.50f;
    printf("学生3: %s, %d岁, GPA: %.2f\n", s3.name, s3.age, s3.gpa);
    // 输出: 学生3: 王五, 19岁, GPA: 3.50

    return 0;
}

12.3 指定初始化器(C99):“点名式"赋值

为什么需要指定初始化器?

想象一下这个场景:你要初始化一个数组,其中第 0 个元素是 0,第 5 个元素是 100,其他都是 99。

用传统方法:

1
2
int arr[10] = {0, 99, 99, 99, 99, 100, 99, 99, 99, 99};
// 写这么多 99,眼睛都花了!

有了指定初始化器(C99 引入的特性),你可以"点名"赋值:

1
2
3
int arr[10] = {[0] = 0, [5] = 100, [1] = 99, [2] = 99, [3] = 99, [4] = 99,
               [6] = 99, [7] = 99, [8] = 99, [9] = 99};
// 看起来更清晰了!

数组的指定初始化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main() {
    // 指定初始化器:想给谁赋值就写谁的下标
    int arr[10] = {[0] = 0, [5] = 100, [9] = 200};

    // 其他未指定的元素自动为 0!
    printf("arr[0] = %d\n", arr[0]);  // 输出: 0
    printf("arr[1] = %d\n", arr[1]);  // 输出: 0(自动初始化为0)
    printf("arr[5] = %d\n", arr[5]);  // 输出: 100
    printf("arr[9] = %d\n", arr[9]);  // 输出: 200

    // 混合使用:指定 + 顺序
    int mixed[10] = {1, 2, [5] = 50, 3, 4};  // {1, 2, 0, 0, 50, 3, 4, 0, 0, 0}? 不对!

    printf("\nmixed 数组内容:\n");
    for (int i = 0; i < 10; i++) {
        printf("arr[%d] = %d\n", i, mixed[i]);
    }

    return 0;
}

结构体的指定初始化

指定初始化器在结构体中更加强大——你可以不按顺序初始化成员!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>

struct Point {
    int x;
    int y;
    int z;
};

struct Rectangle {
    struct Point top_left;
    struct Point bottom_right;
};

int main() {
    // 乱序初始化结构体?不存在的!
    struct Point p = {.y = 10, .x = 5, .z = 15};

    printf("点坐标: (%d, %d, %d)\n", p.x, p.y, p.z);
    // 输出: 点坐标: (5, 10, 15)

    // 嵌套结构体的指定初始化
    struct Rectangle rect = {
        .top_left = {.x = 0, .y = 10},
        .bottom_right = {.x = 20, .y = 0}
    };

    printf("矩形左上: (%d, %d)\n", rect.top_left.x, rect.top_left.y);
    // 输出: 矩形左上: (0, 10)
    printf("矩形右下: (%d, %d)\n", rect.bottom_right.x, rect.bottom_right.y);
    // 输出: 矩形右下: (20, 0)

    return 0;
}

指定初始化器的优势

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>

// 假设我们要初始化一个星期的编码映射
int main() {
    // 传统方式:数位置
    // int day_code[7] = {1, 2, 3, 4, 5, 6, 7};  // Monday=1, Tuesday=2, ...

    // 指定初始化器:清晰明了!
    int day_code[7] = {
        [0] = 1,  // Monday
        [1] = 2,  // Tuesday
        [2] = 3,  // Wednesday
        [3] = 4,  // Thursday
        [4] = 5,  // Friday
        [5] = 6,  // Saturday
        [6] = 7   // Sunday
    };

    // 现在你想知道 Friday 对应几?直接看 [4] = 5,一目了然!
    printf("Friday code: %d\n", day_code[4]);  // 输出: 5

    // 如果哪天顺序变了(比如周日变成第一天)
    // 只需要改一处,不用改整个数组!
    return 0;
}

12.4 复合字面量(C99):匿名变量的大招

什么是复合字面量?

复合字面量(Compound Literal)是 C99 引入的一个"黑科技”——它允许你在表达式中直接创建一个数组或结构体,而不用先给它们起个名字

听起来有点抽象?让我用生活中的例子解释:

正常情况下,你去奶茶店点单,要先说"我要一杯奶茶"(声明变量),然后店员给你做(赋值)。但复合字面量就像是——你直接喊"给我一杯奶茶,多加珍珠!“然后当场喝掉,不用给它起名字。

数组的复合字面量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

int main() {
    // 普通方式:先声明,再赋值
    int arr1[3] = {1, 2, 3};

    // 复合字面量:直接创建匿名数组
    int *ptr = (int[]){4, 5, 6};  // (int[]) 是类型标识,后面跟初始化列表

    // 使用指针访问
    printf("ptr[0] = %d\n", ptr[0]);  // 输出: 4
    printf("ptr[1] = %d\n", ptr[1]);  // 输出: 5
    printf("ptr[2] = %d\n", ptr[2]);  // 输出: 6

    // 复合字面量的常用场景:作为函数参数
    int sum = 0;
    int values[3] = {10, 20, 30};
    for (int i = 0; i < 3; i++) {
        sum += values[i];
    }
    printf("sum = %d\n", sum);  // 输出: 60

    return 0;
}

结构体的复合字面量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

struct Point {
    int x;
    int y;
};

void print_point(struct Point p) {
    printf("点坐标: (%d, %d)\n", p.x, p.y);
}

int main() {
    // 普通方式:先命名,再传参
    struct Point p1 = {10, 20};
    print_point(p1);
    // 输出: 点坐标: (10, 20)

    // 复合字面量:直接作为参数传递,不用命名!
    print_point((struct Point){30, 40});
    // 输出: 点坐标: (30, 40)

    // 还可以用指定初始化器
    print_point((struct Point){.y = 50, .x = 60});
    // 输出: 点坐标: (60, 50)

    return 0;
}

复合字面量的实际应用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>

struct Matrix {
    int rows;
    int cols;
    int data[3][3];
};

void print_matrix(struct Matrix m) {
    printf("矩阵 (%dx%d):\n", m.rows, m.cols);
    for (int i = 0; i < m.rows; i++) {
        for (int j = 0; j < m.cols; j++) {
            printf("%4d ", m.data[i][j]);
        }
        printf("\n");
    }
}

int main() {
    // 场景一:临时创建一个矩阵用于计算
    print_matrix((struct Matrix){
        .rows = 3,
        .cols = 3,
        .data = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
    });
    // 输出一个 3x3 矩阵

    // 场景二:函数返回临时结构体
    struct Point create_point(int x, int y) {
        return (struct Point){x, y};  // 直接返回复合字面量!
    }

    struct Point center = create_point(100, 200);
    printf("中心点: (%d, %d)\n", center.x, center.y);
    // 输出: 中心点: (100, 200)

    return 0;
}

12.5 隐式初始化规则:内存的"潜规则”

这是 C 语言中最容易踩坑的地方之一!

想象一下,你去酒店入住,前台说"房间已经给你留好了"。结果你推开门,发现里面住着陌生人(垃圾值)!这就是未初始化局部变量的恐怖之处。

全局变量和静态变量的"特殊待遇"

在 C 语言中,全局变量(在函数外部声明)和静态变量(用 static 关键字声明)会自动被初始化为

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>

// 全局变量(自动初始化为 0)
int global_int;
float global_float;
char global_char;
int *global_ptr;

void test_static() {
    // 静态变量(自动初始化为 0)
    static int static_int;
    static float static_float;

    printf("函数内的静态变量:\n");
    printf("  static_int = %d\n", static_int);   // 输出: 0
    printf("  static_float = %.2f\n", static_float);  // 输出: 0.00
}

int main() {
    printf("全局变量(自动零初始化):\n");
    printf("  global_int = %d\n", global_int);      // 输出: 0
    printf("  global_float = %.2f\n", global_float); // 输出: 0.00
    printf("  global_char = '%c'\n", global_char);   // 输出: ''
    printf("  global_ptr = %p\n", (void*)global_ptr); // 输出: (nil)

    test_static();

    return 0;
}

局部变量的"野孩子"待遇

重要的事情说三遍:

局部变量不会自动初始化! 局部变量不会自动初始化! 局部变量不会自动初始化!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

void show_garbage() {
    // 局部变量(不初始化!)
    int x;
    int y;
    int z;

    // 打印"垃圾值"
    printf("局部的 x = %d\n", x);
    printf("局部的 y = %d\n", y);
    printf("局部的 z = %d\n", z);
}

int main() {
    // 全局变量
    int global = 999;
    printf("全局变量 global = %d(永远是 0)\n", global);

    // 局部变量 —— 危险地带!
    int local;
    printf("局部变量 local = %d(可能是任何值!)\n", local);

    // 演示垃圾值的"随机性"
    printf("\n连续调用三次 show_garbage():\n");
    show_garbage();
    printf("---\n");
    show_garbage();
    printf("---\n");
    show_garbage();

    return 0;
}

常见错误场景

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>

int main() {
    // 错误示例 1:累加器忘记初始化
    int sum;
    int numbers[] = {1, 2, 3, 4, 5};

    // 以为 sum 初始值是 0,实际是垃圾值!
    for (int i = 0; i < 5; i++) {
        sum += numbers[i];  // sum 未初始化,可能是任意值
    }
    printf("sum = %d(应该是 15,但如果 sum 初始是 10086...)\n", sum);

    // 正确做法:初始化为 0
    sum = 0;  // 必须初始化!
    for (int i = 0; i < 5; i++) {
        sum += numbers[i];
    }
    printf("sum = %d(正确!应该是 15)\n", sum);

    // 错误示例 2:指针忘记初始化
    int *ptr;
    // printf("%d\n", *ptr);  // 危险!ptr 没有初始化,指向未知地址!

    // 正确做法
    int value = 42;
    ptr = &value;  // 让指针指向一个有效的地址
    printf("*ptr = %d(安全!)\n", *ptr);  // 输出: 42

    return 0;
}

初始化规则总结表

变量类型位置初始值举例
全局变量函数外部零 (0)int g_x;
静态变量函数内部零 (0)static int s_x;
局部变量函数内部垃圾值!int l_x;
指针变量函数内部垃圾值(野指针)!int *p;

12.6 字符串数组初始化:字符数组的特殊待遇

字符数组 vs 普通数组

字符串在 C 语言中是一个特殊的存在——它是 char 类型的数组,但有一些"专属特权"。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main() {
    // 方式一:逐个字符初始化(普通数组方式)
    char s1[] = {'H', 'e', 'l', 'l', 'o', '\0'};  // 必须手动加 '\0'!

    // 方式二:用字符串字面量初始化(字符串专属方式!)
    char s2[] = "Hello";  // 自动包含 '\0',不用手动写

    printf("s1 = %s\n", s1);  // 输出: Hello
    printf("s2 = %s\n", s2);  // 输出: Hello

    // 长度对比
    printf("s1 长度: %lu\n", sizeof(s1) / sizeof(char));  // 输出: 6 (5个字符 + '\0')
    printf("s2 长度: %lu\n", sizeof(s2) / sizeof(char));  // 输出: 6 (5个字符 + '\0')

    return 0;
}

字符串初始化的各种姿势

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>

int main() {
    // 姿势一:指定大小,多余的位置自动填 '\0'
    char s1[10] = "Hi";
    printf("s1: "); for (int i = 0; i < 10; i++) printf("[%c]", s1[i]);
    // 输出: s1: [H][i][\0][\0][\0][\0][\0][\0][\0][\0]

    // 姿势二:自动计算长度
    char s2[] = "Hello";
    printf("\ns2 大小: %lu (自动计算)\n", sizeof(s2));  // 输出: 6

    // 姿势三:字符逐个初始化(必须自己加 '\0')
    char s3[6] = {'W', 'o', 'r', 'l', 'd', '\0'};

    // 姿势四:只初始化第一个字符,其余自动为 '\0'
    char s4[5] = {'A'};
    printf("s4: ");
    for (int i = 0; i < 5; i++) {
        printf("[%d]", s4[i]);  // 输出: [65][0][0][0][0]
    }

    return 0;
}

字符串数组的经典错误

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main() {
    // 错误:忘记 '\0' 结束符
    char bad[] = {'H', 'e', 'l', 'l', 'o'};  // 没有 '\0'!
    // printf("%s\n", bad);  // 危险!会一直打印直到遇到 '\0'!

    // 正确:手动添加 '\0'
    char good[] = {'H', 'e', 'l', 'l', 'o', '\0'};

    // 最推荐:使用字符串字面量
    char best[] = "Hello";  // 自动包含 '\0'

    printf("字符串: %s\n", best);  // 输出: Hello

    return 0;
}

12.7 const 与初始化:常量必须"一出生就定型"

const 是什么?

const 是 C 语言中用于声明"常量"的关键字。一旦一个变量被 const 修饰,它就再也不能被修改了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

int main() {
    const int CONSTANT_VALUE = 100;
    // CONSTANT_VALUE = 200;  // 错误!不能修改 const 变量!

    printf("常量值: %d\n", CONSTANT_VALUE);  // 输出: 100

    return 0;
}

const 变量的初始化规则

const 变量必须在声明时初始化! 因为一旦声明为 const,就再也不能赋值了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

int main() {
    // 正确:声明时初始化
    const int a = 10;
    const float b = 3.14f;
    const char c = 'A';

    printf("a=%d, b=%.2f, c=%c\n", a, b, c);

    // 错误:先声明后初始化(编译错误!)
    // const int d;  // 错误!const 变量必须在声明时初始化!
    // d = 20;

    // const 指针的初始化
    int value = 42;
    const int *ptr1 = &value;  // 指向常量的指针
    int *const ptr2 = &value;  // 常量指针(指针本身不能变)

    printf("*ptr1=%d, *ptr2=%d\n", *ptr1, *ptr2);

    return 0;
}

静态初始化 vs 运行时初始化

这是理解 const 行为的关键!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

// 场景一:const 全局变量必须在编译时就能确定值
// 编译器需要在编译阶段就把值"刻进"二进制文件里
const int COMPILE_TIME_VALUE = 123;  // 编译时已知 → OK

// const int RUN_TIME_VALUE;  // 错误!运行时才能确定 → 不行!

int main() {
    // 场景二:const 局部变量可以是运行时计算的结果
    int input;
    printf("请输入一个数字: ");
    scanf("%d", &input);

    const int run_time = input;  // 运行时才知道值 → OK

    printf("运行时 const 值: %d\n", run_time);

    // 注意:const 只保证"不能修改",不保证"编译时确定"
    return 0;
}

const 的真正意义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

// 用 const 提高代码可读性和安全性

// 方式一:不用 const
double calculate_circle_area(double radius) {
    // radius 能改吗?不知道!
    // 会不会被不小心改掉?不知道!
    return 3.14159 * radius * radius;
}

// 方式二:用 const 声明"这个参数我不会改"
double calculate_circle_area_const(double radius) {
    const double pi = 3.14159;  // 这个 pi 不会变
    const double r = radius;     // 这个 r 也不会变(只是复制)

    // radius = 999;  // 如果取消注释,编译错误!保护了原始数据

    return pi * r * r;
}

int main() {
    printf("面积: %.2f\n", calculate_circle_area_const(5.0));
    // 输出: 面积: 78.54

    return 0;
}

本章小结

恭喜你完成了"初始化"这一章的学习!让我们来回顾一下今天学到的核心知识点:

🎯 关键知识点

  1. 标量初始化:在声明时直接给单一值变量赋值,如 int x = 42;。未初始化的局部变量是"垃圾值"——一个完全不可预测的随机数。

  2. 聚合初始化:用于数组和结构体。数组部分初始化时,未指定的元素自动为零;结构体可以按顺序或用指定初始化器(C99)不按顺序初始化。

  3. 指定初始化器(C99):使用 [index].member 语法精准赋值,如 int arr[5] = {[2] = 10};,让代码更清晰,不易出错。

  4. 复合字面量(C99):创建匿名数组或结构体,如 (int[]){1, 2, 3},非常适合临时数据或函数参数传递。

  5. 隐式初始化规则:全局变量和静态变量自动初始化为 0,但局部变量不会自动初始化,必须手动赋值!这是 C 语言最常见的 bug 来源之一。

  6. 字符串数组初始化char s[] = "hello"; 是最简洁的方式,编译器会自动处理 '\0' 结束符。

  7. const 与初始化const 变量必须在声明时初始化(编译时常量),或者在运行时通过计算赋值(但一旦赋值就不能再改)。

💡 编程习惯建议

养成为每个变量显式初始化的好习惯! 即使你知道它会被后续赋值,提前初始化也是一个好习惯,能让代码更安全,也让阅读代码的人更清楚你的意图。

⚠️ 避坑提醒

  • 永远不要假设局部变量会自动初始化为零
  • 永远不要假设别人会记得初始化变量
  • 使用 -Wall -Wextra 编译器警告标志,它能帮你发现未初始化的变量

第 12 章:初始化 —— 给变量一个"人生起点" 到此结束!

下一章我们将探讨 C 语言的更多高级特性。继续保持这份热情,你已经比 90% 的 C 语言学习者更懂得如何写出安全的代码了! 💪

记住:一个好的起点,决定了变量的一生!

最后修改 March 29, 2026: 新增 C 教程 (93a26d7)