跳转至

导学案8 · 结构体:把数据捆成一个整体

前置要求:导学案7(会指针、会数组作函数参数) 参考课程:浙江大学 翁恺《C 语言程序设计》第 11 章(结构类型) 预计用时:1 小时(按需自取) 本节目标:会定义结构体、声明结构体变量和数组、用 . 和 -> 访问成员、把结构体传给函数


8.0 序

数组有个硬限制:只能装同一种类型。

但现实里的东西从来不是单一类型的。描述一个学生,要学号(int)、姓名(字符数组)、成绩(double);描述一条激光雷达数据,要 x、y、偏航角、有效标志。用散变量写,5 个学生就是 15 个变量,还容易张冠李戴。

结构体就是来解决这件事的:把不同类型的数据捆在一起,起一个名字,变成一种"自定义类型"。 从此"一个学生"就是一个变量,"一班学生"就是一个数组。

飞控代码里更是如此:每个传感器的数据包都是一个结构体。你在 飞控主控代码 里看到的 rplidar_st.x、rplidar_st.yaw,就是"结构体变量点成员"。

本节路线:定义与使用 → 结构体数组 → 结构体与函数 → 练习


8.1 定义与使用

8.1.1 先画图纸,再造房子

struct Student {             /* 定义一个自定义类型:图纸 */
    int id;
    char name[20];
    double score;
};                           /* 注意:这个分号不能少 */
struct Student s = {1001, "张三", 92.5};   /* 声明变量:按图纸造房子,并初始化 */
  • 定义结构体不占内存,它只是告诉编译器"这种类型长什么样";声明变量才真的占内存。
  • 定义末尾的分号不能少,这是新手最常漏的一个分号。
  • 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;
}

输出:

学号:1001
姓名:张三
成绩:92.5
改完之后成绩:95.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;
}

输出:

a = (3, 5)
b = (0, 0)

两种访问成员的写法别混:

手里拿的是 写法 例子
结构体变量 . rplidar_st.x
结构体指针 -> p->x

结构体往往有几十个字节,整份复制又慢又费内存,所以真实代码里几乎总是传指针:飞控主控代码 里的 &ModeChangedFlag、各种 &xxx_st 都是这个套路。


8.3 综合练习

本节练习用于自练与自查,不强制完成、不需要提交;需要提交的是实验室统一布置的作业(作业一 / 作业二)。 参考代码统一放在本节末尾,做完之前不要翻。

  1. 定义 struct Book { char title[30]; double price; int stock; },存 3 本书,打印最贵那本的书名和价格。
  2. 【作业二第 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;。
  3. 定义 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 定义结构体等内容,我觉得可以减轻学生负担,本节应该内容比较少,如果删多了可能会导致知识不连贯,欢迎读者反馈