导学案8 · 结构体:把数据捆成一个整体¶
前置要求:导学案7(会指针、会数组作函数参数) 参考课程:浙江大学 翁恺《C 语言程序设计》第 11 章(结构类型) 预计用时:1 小时(按需自取) 本节目标:会定义结构体、声明结构体变量和数组、用
.和->访问成员、把结构体传给函数
8.0 序¶
数组有个硬限制:只能装同一种类型。
但现实里的东西从来不是单一类型的。描述一个学生,要学号(int)、姓名(字符数组)、成绩(double);描述一条激光雷达数据,要 x、y、偏航角、有效标志。用散变量写,5 个学生就是 15 个变量,还容易张冠李戴。
结构体就是来解决这件事的:把不同类型的数据捆在一起,起一个名字,变成一种"自定义类型"。 从此"一个学生"就是一个变量,"一班学生"就是一个数组。
飞控代码里更是如此:每个传感器的数据包都是一个结构体。你在 飞控主控代码 里看到的 rplidar_st.x、rplidar_st.yaw,就是"结构体变量点成员"。
本节路线:定义与使用 → 结构体数组 → 结构体与函数 → 练习
8.1 定义与使用¶
8.1.1 先画图纸,再造房子¶
- 定义结构体不占内存,它只是告诉编译器"这种类型长什么样";声明变量才真的占内存。
- 定义末尾的分号不能少,这是新手最常漏的一个分号。
- C 语言里声明变量要带
struct前缀(struct Student s;)。以后你会见到不带前缀的写法,那是typedef起的别名。
8.1.2 用 . 访问成员¶
#include <stdio.h>
struct Student {
int id;
char name[20];
double score;
};
int main(void)
{
struct Student s = {1001, "张三", 92.5};
printf("学号:%d\n", s.id);
printf("姓名:%s\n", s.name);
printf("成绩:%.1f\n", s.score);
s.score = 95.0; /* 成员可以单独读、单独改 */
printf("改完之后成绩:%.1f\n", s.score);
return 0;
}
输出:
成员就是普通的变量,只是前面多了一个 s.。s.name 是字符数组,所以用 %s 打印、用 scanf("%s", s.name) 读入(不写 &,导学案7讲过原因)。
8.2 结构体与函数¶
8.2.1 传给函数:想改就传指针,成员用 ->¶
结构体传给函数同样是值传递(整份复制)。想改到原件,就传地址;这时访问成员要用箭头 ->:
#include <stdio.h>
struct Point {
int x;
int y;
};
void move(struct Point *p, int dx, int dy)
{
p->x += dx; /* 等价于 (*p).x += dx */
p->y += dy;
}
int main(void)
{
struct Point a = {0, 0};
struct Point b = a; /* b 是 a 的一份拷贝 */
move(&a, 3, 5);
printf("a = (%d, %d)\n", a.x, a.y);
printf("b = (%d, %d)\n", b.x, b.y);
return 0;
}
输出:
两种访问成员的写法别混:
| 手里拿的是 | 写法 | 例子 |
|---|---|---|
| 结构体变量 | . |
rplidar_st.x |
| 结构体指针 | -> |
p->x |
结构体往往有几十个字节,整份复制又慢又费内存,所以真实代码里几乎总是传指针:飞控主控代码 里的 &ModeChangedFlag、各种 &xxx_st 都是这个套路。
8.3 综合练习¶
本节练习用于自练与自查,不强制完成、不需要提交;需要提交的是实验室统一布置的作业(作业一 / 作业二)。 参考代码统一放在本节末尾,做完之前不要翻。
- 定义
struct Book { char title[30]; double price; int stock; },存 3 本书,打印最贵那本的书名和价格。 - 【作业二第 4 题】 定义
struct Student { int id; int score; },用rand()生成 5 名学生的学号(100~110)和成绩(0~100),按成绩从高到低排序后输出。提示:交换两个结构体可以整体赋值,struct Student t = a[i]; a[i] = a[j]; a[j] = t;。 - 定义
struct Point { double x; double y; },写函数double dist(struct Point a, struct Point b)求两点距离(用sqrt),主函数读入两点并输出(保留两位)。这是作业一第 7 题的地基。 4.(选做)【作业二第 5 题】 判断线段 AB 是否穿过以 C 为球心、R 为半径的球:求 C 到线段 AB 的最短距离 d,若 d < R 则输出"遮挡",否则输出"未被遮挡"。提示:先求垂足,注意垂足可能落在线段之外,要分三种情况讨论。
8.4 本节小结与自查¶
学完本节,你应该能做到:
- 定义一个结构体类型并声明变量,知道定义末尾的分号不能少
- 用
{...}初始化结构体,用.读写成员 - 把结构体指针传给函数,用
->访问成员,说清.和->分别在什么时候用
本轮到此为止。 顺序、选择、循环、数组、函数、指针、结构体——C 语言入门需要的骨架已经齐了。 剩下的(字符串处理、多文件工程、位运算、枚举)都是"用到再查"的东西,有需要我们会要求掌握的,但不是现在。
8.5 练习参考代码(做完再看)¶
练习 1 参考代码¶
#include <stdio.h>
struct Book {
char title[30];
double price;
int stock;
};
int main(void)
{
struct Book books[3] = {
{"C语言程序设计", 39.0, 12},
{"嵌入式系统设计", 59.5, 5},
{"自动控制原理", 45.0, 8}
};
int best = 0;
for (int i = 1; i < 3; i++) {
if (books[i].price > books[best].price) {
best = i;
}
}
printf("最贵的是《%s》,%.1f 元\n", books[best].title, books[best].price);
return 0;
}
输出:最贵的是《嵌入式系统设计》,59.5 元
练习 2 参考代码¶
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct Student {
int id;
int score;
};
int main(void)
{
struct Student cls[5];
srand((unsigned)time(NULL));
for (int i = 0; i < 5; i++) {
cls[i].id = rand() % 11 + 100; /* 100 ~ 110 */
cls[i].score = rand() % 101; /* 0 ~ 100 */
}
/* 冒泡排序:成绩从高到低 */
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4 - i; j++) {
if (cls[j].score < cls[j + 1].score) {
struct Student t = cls[j];
cls[j] = cls[j + 1];
cls[j + 1] = t;
}
}
}
printf("学号 成绩\n");
for (int i = 0; i < 5; i++) {
printf("%d %d\n", cls[i].id, cls[i].score);
}
return 0;
}
学号是随机取的,可能重复,想想怎么优化。
练习 3 参考代码¶
#include <stdio.h>
#include <math.h>
struct Point {
double x;
double y;
};
double dist(struct Point a, struct Point b)
{
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
int main(void)
{
struct Point a = {0.0, 0.0};
struct Point b = {0.0, 0.0};
printf("请输入 A 点坐标:");
scanf("%lf %lf", &a.x, &a.y);
printf("请输入 B 点坐标:");
scanf("%lf %lf", &b.x, &b.y);
printf("距离 = %.2f\n", dist(a, b));
return 0;
}
结构体是 C 语言里第一次出现"自定义类型"。从这一节起,你定义变量时想的不只是"它是什么类型",而是"它描述的是现实里的什么东西"。这个转变,就是从"会写 C"到"会用 C 建模"的第一步。
作者:Mike-lin300
最后更新:2026-09-25
更新说明:删除了多个小节,包括结构体数组、typedef 定义结构体等内容,我觉得可以减轻学生负担,本节应该内容比较少,如果删多了可能会导致知识不连贯,欢迎读者反馈