← C 语言 学习路线(共 8 章)

第 7 章 结构体、枚举与 typedef

red wenzi · 2026-09-15 · 编程语言 · C 语言 · 📖 预计阅读 12 分钟 · 共 2 道练习
🎯 本章你会学到:把相关数据打包成一个类型,并用 qsort 排序结构体数组。建议边读边敲,每节练习先自己做,再展开答案对照。

结构体让你把“一个学生的姓名和分数”这种相关数据打包成一个整体。这是从“处理变量”走向“处理对象”的第一步,也是链表、树的起点。

定义与访问

结构体与指针访问
#include <stdio.h>

typedef struct {          /* typedef 让后面可以只写 Point */
    double x;
    double y;
} Point;

static double dist2(const Point *p) {   /* 传指针:不拷贝整个结构体 */
    return p->x * p->x + p->y * p->y;
}

int main(void) {
    Point p = {3.0, 4.0};               /* 初始化列表 */
    printf("%.1f\n", dist2(&p));
    return 0;
}
运行结果
25.0

输出 25.0。两个语法要分清:p.x 用于结构体变量本身,p->x 等价于 (*p).x,用于指向结构体的指针。

结构体数组与 qsort

按分数排序结构体数组
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char name[32];
    int score;
} Student;

/* qsort 的比较函数:负数表示 a 排在 b 前面 */
static int cmp_score_desc(const void *a, const void *b) {
    const Student *x = a;
    const Student *y = b;
    if (x->score != y->score) {
        return y->score - x->score;     /* 分数降序 */
    }
    return strcmp(x->name, y->name);    /* 同分按名字升序 */
}

int main(void) {
    Student s[3] = {{"Ann", 90}, {"Bob", 95}, {"Cid", 88}};
    qsort(s, 3, sizeof s[0], cmp_score_desc);
    for (size_t i = 0; i < 3; ++i) {
        printf("%s %d\n", s[i].name, s[i].score);
    }
    return 0;
}
运行结果
Bob 95
Ann 90
Cid 88

输出顺序是 Bob 95Ann 90Cid 88qsort 是标准库的通用排序,需要你告诉它“每个元素多大”和“怎么比”。

写法含义什么时候用
struct Point p;结构体变量需要独立一份数据时
Point *p = &other;结构体指针传参、动态分配时(避免整体拷贝)
p.x / p->x访问成员变量用点,指针用箭头
typedef struct {...} Point;起别名让类型名更像普通类型(推荐)
⚠️ 易错点 结构体按值传参会整体拷贝。结构体很大时(比如里面有数组)要传指针,并用 const 表示只读。
⚠️ 易错点 strcmp 返回的是“差值”,不是 1/0/-1。想判断相等要写 strcmp(a, b) == 0

✍️ 本节练习

7.1必做按分数排序输出学生

定义 Student(名字 + 分数),读入 3 个学生,按分数从高到低输出姓名和分数。

输入 三行,每行「名字 分数」。

输出 三行,按分数降序。

样例输入
Ann 90
Bob 95
Cid 88
样例输出
Bob 95
Ann 90
Cid 88

💡 提示 比较函数里返回 y->score - x->score 就是降序。

✅ 查看参考答案与解析
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    char name[32];
    int score;
} Student;

static int cmp_desc(const void *a, const void *b) {
    const Student *x = a;
    const Student *y = b;
    return y->score - x->score;
}

int main(void) {
    Student s[3];
    for (int i = 0; i < 3; ++i) {
        if (scanf("%31s %d", s[i].name, &s[i].score) != 2) {
            return 1;
        }
    }
    qsort(s, 3, sizeof s[0], cmp_desc);
    for (int i = 0; i < 3; ++i) {
        printf("%s %d\n", s[i].name, s[i].score);
    }
    return 0;
}

解析 %31s 限宽保证名字不会写坏 name[32];比较函数参数是 const void *,要先转成 const Student *

7.2挑战平均分与最高分

读入 n 个学生(每行「名字 分数」),输出人数、平均分(保留 2 位小数)和最高分学生的名字。

输入 第一行 n;接下来 n 行「名字 分数」。

输出 三行:count=avg=top=

样例输入
3
Ann 90
Bob 95
Cid 88
样例输出
count=3
avg=91.00
top=Bob

💡 提示 一边读一边累加分数、维护最高分;最高分要顺便把名字复制下来。

✅ 查看参考答案与解析
#include <stdio.h>
#include <string.h>

#define MAXN 100

typedef struct {
    char name[32];
    int score;
} Student;

int main(void) {
    int n = 0;
    if (scanf("%d", &n) != 1 || n <= 0 || n > MAXN) {
        return 1;
    }
    Student s[MAXN];
    long long sum = 0;
    int best = -1, best_idx = 0;
    for (int i = 0; i < n; ++i) {
        if (scanf("%31s %d", s[i].name, &s[i].score) != 2) {
            return 1;
        }
        sum += s[i].score;
        if (s[i].score > best) {
            best = s[i].score;
            best_idx = i;
        }
    }
    printf("count=%d\n", n);
    printf("avg=%.2f\n", (double)sum / n);
    printf("top=%s\n", s[best_idx].name);
    return 0;
}

解析 记录最高分的下标而不是名字数组,最后按下标取名字,代码更短也不容易出错。

📚 本文概念都在知识大全:

C 语言 · GCC 编译器 · 指针 · 动态内存 malloc/free · C 字符串 · 结构体 · 预处理 · 编译与链接