2-3 结构体 --- 把不同类型打包在一起
⏱ 本节预计用时:25分钟
你前面学了数组------一次装很多个同类型 的数据。比如 int scores[10],10个格子装的都是 int,很整齐。
但现实中的"一组数据",往往不是同类型的。
井字棋的"游戏状态"包含棋盘(char数组)、当前玩家(int)、胜负状态(int)。学生管理系统里,一个学生有姓名(char数组)、学号(int)、成绩(int数组)、平均分(float)。
这些数据类型各不相同,但它们逻辑上是一个整体。 如果把它们拆成零散的变量到处放,代码一长你就找不到谁是谁了。
这一节,教你用「结构体」把它们打包成一个整体。
1 为什么需要结构体
你有一堆零散的纸:一张写着"张三",一张写着"1001",一张写着"85分"。你把这堆纸往桌上一放------一阵风吹来,"张三"和"85分"就分开了。你不知道哪张纸和哪张纸是一组的。
结构体,就是一个档案袋。 它把属于同一个东西的纸装在一起:
- 这个袋子上写着"学生张三"
- 打开袋子,学号那张纸在里面,成绩那张纸也在里面
- 不会丢,不会混
来对比一下。
不用结构体------三样东西散落各处:
c
char name[20] = "张三";
int id = 1001;
int score = 85;
// 后来代码里有三个学生...
char name2[20] = "李四";
int id2 = 1002;
int score2 = 92;
char name3[20] = "王五";
int id3 = 1003;
int score3 = 78;
看出来问题了吗?每多一个学生,就要多三个变量。 而且 name、id、score 之间的联系全靠你的脑子记------"这个 id2 是对应 name2 的,别搞混了"。
再来看看井字棋。没有结构体时,游戏状态是三个孤零零的变量:
c
char board[9]; // 棋盘在哪?
int currentPlayer; // 该谁下了?
int gameStatus; // 游戏结束了吗?
把它们当作参数传给函数的时候,你需要写三个参数;哪天想加一个新属性(比如"移动次数"),所有函数签名都得改------漏改一个就是bug。
用结构体------一个袋子全装好:
c
struct Game {
char board[9]; // 9个格子的棋盘
int currentPlayer; // 当前玩家:1或2
int gameStatus; // 0=进行中 1=玩家1胜 2=玩家2胜 3=平局
};
struct Game game; // 一个变量 = 整个游戏状态
棋盘、当前玩家、游戏状态全在里面,想取什么就从袋子里拿,这时的struct Game就像int、double一样,也是一种数据类型!还记得之前讲数据类型时的格子比喻吗?这是你人为创造的,由很多个小格子组成的新的大格子!
2 结构体变量的声明和初始化
2.1 定义结构体变量
定义一个结构体,就像设计一个档案袋的规格:
c
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// ... 更多成员
}; // 注意:这里有分号
注意 :定义结构体只是画图纸,不是造东西。写在
struct { ... };里的代码不会被执行,它只是在告诉编译器:"以后可能有这样一种袋子,里面装这些东西。"
图纸画好了,我们来造个实物:学生结构体。
c
struct Student {
int id; // 学号
int scores[3]; // 三科成绩
double average; // 平均分
};
2.2 声明结构体变量
声明结构体变量和声明普通变量一样,只是类型名前面要加 struct:
c
struct Student stu; // 造一个Student类型的变量,叫stu,这个struct必须要写,不然会报错。是不是觉得写struct有点啰嗦?下一节会教你怎么用typedef把它省掉。
读作:"struct Student 类型的变量 stu"。
2.3 三种初始化方式
方式一:先声明,后逐个赋值
c
struct Student s1;
s1.id = 1001; // .的意思是"的",学生s1的id是1001。
s1.scores[0] = 85; // s1的第一科成绩是85。
方式二:定义时用花括号按顺序初始化
c
struct Student s1 = {1001, {85, 90, 78}, 84.33f};
顺序必须和定义结构体时成员出现的顺序一模一样 。第一个是 name,第二个是 id,第三个是 scores(数组用内层花括号),第四个是 average。
方式三:指定成员名初始化(推荐,不会搞错顺序)
c
struct Student s1 = {
.id = 1001,
.scores = {85, 90, 78},
.average = 84.33f
};
完整示例:
c
#include <stdio.h>
// 1. 定义学生结构体(图纸)
struct Student {
int id; // 学号
int scores[3]; // 三科成绩
double average; // 平均分
};
// 封装打印学生信息函数,复用代码
void printStudent(struct Student s) {
printf("===== 学生信息 =====\n");
printf("学号:%d\n", s.id);
printf("三科成绩:%d %d %d\n", s.scores[0], s.scores[1], s.scores[2]);
printf("平均分:%.2lf\n\n", s.average);
}
int main() {
// ==================== 方式1:先声明变量,后逐个赋值 ====================
struct Student s1;
s1.id = 1001;
s1.scores[0] = 85;
s1.scores[1] = 90;
s1.scores[2] = 78;
s1.average = 84.33;
printf("【方式1:逐个赋值初始化】\n");
printStudent(s1);
// ==================== 方式2:定义时按顺序整体初始化 ====================
struct Student s2 = {1002, {92, 88, 95}, 91.67};
printf("【方式2:顺序花括号初始化】\n");
printStudent(s2);
// ==================== 方式3:指定成员名初始化(推荐,顺序无关) ====================
struct Student s3 = {
.average = 76.00,
.id = 1003,
.scores = {70, 75, 83}
};
printf("【方式3:指定成员初始化】\n");
printStudent(s3);
return 0;
}
运行结果:

3 结构体数组
一个学生用一个 struct Student。那全班50个学生呢?
用struct Student的数组!
c
struct Student class[50]; // 50个Student的数组
如图示,每个 class[i] 本身都是一个完整的 struct Student。
c
class[0] → { id:1001, scores:{85,90,78}, average:84.33 }
class[1] → { id:1002, scores:{92,88,95}, average:91.67 }
class[2] → { id:1003, scores:{78,82,80}, average:80.00 }
...
class[49] → { ... }
用循环遍历:
c
struct Student class[3] = {
{ 1001, {85, 90, 78}, 84.33f},
{ 1002, {92, 88, 95}, 91.67f},
{ 1003, {78, 82, 80}, 80.00f}
};
for (int i = 0; i < 3; i++) {
printf(" 学生%d:\n", i + 1);
printf(" 学号:%d\n", class[i].id);
printf(" 成绩:%d, %d, %d\n",
class[i].scores[0],
class[i].scores[1],
class[i].scores[2]);
printf(" 平均分:%.2f\n\n", class[i].average);
}
注意
class[i].id的读法:class 的第 i 个学生,取他的 id 。.和[i]放在一起不怕,从左往右读就行。

4 总结
struct是把不同类型打包在一起的工具 --- 井字棋的Game包了棋盘+玩家+状态,学生管理的Student包了姓名+学号+成绩+平均分- 用
.访问成员 ---game.board[0]表示"game 的 board 的第0个元素",stu.name表示"stu 的 name" - 定义完
struct就可以像普通类型一样用 ---struct Student s;声明变量,struct Student class[50];声明数组,和你用int、char一模一样,只是多写一个struct关键字 - 结构体数组 = 一次管理多个复杂数据 ---
struct Student class[50]一个变量管50个学生,每个学生又是一个完整的结构体
5 练习题
坚持就是胜利!
题目1:定义学生结构体(学号、成绩),定义结构体数组存储3个学生。键盘输入3个学生信息,最后遍历全部输出。
c
#include <stdio.h>
struct Student {
int id;
int score;
};
int main() {
struct Student stu[3];
// 输入
for(int i = 0; i < 3; i++) {
scanf("%d%d", &stu[i].id, &stu[i].score);
}
// 输出
for(int i = 0; i < 3; i++) {
printf("学号:%d,成绩:%d\n", stu[i].id, stu[i].score);
}
return 0;
}

题目2:定义生日结构体:年、月、日。加到刚才的学生结构体里:学号、生日、分数,初始化一名学生信息并完整打印。
c
#include <stdio.h>
// 嵌套结构体练习
struct Birth {
int year;
int month;
int day;
};
struct Student {
int id;
struct Birth birth;
float score;
};
int main() {
struct Student s = {
.id = 202601,
.birth = {2005, 9, 10},
.score = 88.0
};
printf("学号:%d\n", s.id);
printf("生日:%d年%d月%d日\n", s.birth.year, s.birth.month, s.birth.day);
printf("成绩:%.1f\n", s.score);
return 0;
}
