一个学生有姓名、年龄、成绩,一个点有 x、y 坐标——这些"一组相关数据"用几个独立变量管理会非常凌乱。结构体把它们打包成一个整体,是 C 面向"对象化"思考的第一步。本文从定义讲到联合体,覆盖最常见的用法。

1. 为什么需要结构体

没有结构体时,管理三个学生要写九个变量;有了结构体,一个数组就能装下全班。结构体让代码可读、可扩展、可批量管理

2. 定义与初始化

struct 关键字定义新类型,成员之间用分号分隔:

#include <stdio.h>

struct Student {
    char name[20];
    int age;
    double score;
};

int main(void) {
    struct Student s1 = {"Ada", 18, 95.5};   // 按声明顺序初始化
    struct Student s2;
    s2.age = 19;                              // 逐个赋值
    s2.score = 88.0;

    printf("%s, %d 岁, 成绩 %.1f\n", s1.name, s1.age, s1.score);
    printf("s2: %d 岁, 成绩 %.1f\n", s2.age, s2.score);
    return 0;
}

字符串成员用 strcpy 赋值,不能直接 s1.name = "xxx"

3. 结构体数组

结构体数组 = "一批同构对象",适合存放多条记录。配合循环遍历、求最值:

#include <stdio.h>

struct Student {
    char name[20];
    int age;
    double score;
};

int main(void) {
    struct Student class[3] = {
        {"Ada", 18, 95.5},
        {"Bob", 19, 88.0},
        {"Cindy", 18, 91.0}
    };

    double sum = 0;
    int best = 0;
    for (int i = 0; i < 3; i++) {
        sum += class[i].score;
        if (class[i].score > class[best].score) {
            best = i;
        }
    }
    printf("平均 %.1f, 最高分: %s\n", sum / 3, class[best].name);
    return 0;
}

4. 结构体指针与箭头运算符

结构体较大时,函数传参用指针更高效;通过指针访问成员用 -> 运算符:

#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main(void) {
    struct Point p = {3, 4};
    struct Point *pp = &p;

    printf("(%d, %d)\n", pp->x, pp->y);   // 箭头访问
    (*pp).x = 10;                          // 等价写法
    printf("(%d, %d)\n", pp->x, pp->y);
    return 0;
}

pp->x(*pp).x 的语法糖,两者完全等价。

5. 结构体与函数

结构体可以按值传给函数(整个拷贝),也可以传指针(只传地址)。修改原结构体必须传指针:

#include <stdio.h>

struct Student {
    char name[20];
    int age;
    double score;
};

void raise_score(struct Student *s, double delta) {
    s->score += delta;   // 通过指针修改
}

int main(void) {
    struct Student s = {"Ada", 18, 95.5};
    raise_score(&s, 2.0);
    printf("新成绩: %.1f\n", s.score);   // 97.5
    return 0;
}

6. typedef 与联合体

typedef 给类型起别名,省去每次写 struct 的麻烦;联合体 union 的所有成员共享同一块内存,同一时刻只保存其中一个:

#include <stdio.h>

typedef struct Point {
    int x;
    int y;
} Point;             // 之后直接写 Point

union Data {
    int i;
    float f;
    char c[4];       // 三者占用同一块 4 字节内存
};

int main(void) {
    Point p = {3, 4};      // 不用再写 struct 前缀
    printf("(%d, %d)\n", p.x, p.y);

    union Data d;
    d.i = 65;
    printf("作为 int: %d\n", d.i);
    printf("作为 char[0]: %d\n", d.c[0]);   // 内存重叠的体现
    return 0;
}

联合体常用于节省内存或解析协议数据。结构体大小是成员之和(可能有对齐填充),联合体大小等于最大成员。

💡 学习建议:用 sizeof 打印几个结构体和联合体的大小,思考为什么比"成员大小之和"更大——理解内存对齐,是进阶 C 程序员的必修课。