一个学生有姓名、年龄、成绩,一个点有 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 程序员的必修课。