🎯 本章你会学到:把相关数据打包成一个类型,并用 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 95、Ann 90、Cid 88。qsort 是标准库的通用排序,需要你告诉它“每个元素多大”和“怎么比”。
| 写法 | 含义 | 什么时候用 |
|---|---|---|
| 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;
}解析 记录最高分的下标而不是名字数组,最后按下标取名字,代码更短也不容易出错。