纯代码 + 逐行注释。每段都是完整可编译程序,复制进 Visual Studio 2022 的空项目 .c 文件即可运行。
先看注释,再动手敲;敲完对运行结果。
一、知识点代码
// == 1. 结构体声明与三种定义变量方式 ==
c
#include <stdio.h> // 引入 printf
struct Student // 声明结构体类型
{ int num; // 学号
char name[20]; // 姓名
char sex; // 性别
int age; // 年龄
float score; // 成绩
}; // 分号不能丢
struct Student student1, student2; // 方法1:先声明类型再定义变量
struct Teacher // 方法2:声明类型同时定义变量
{ int num; // 工号
float pay; // 工资
} t1, t2; // 同时定义 t1、t2
struct // 方法3:无名结构体
{ int num; // 成员 num
float pay; // 成员 pay
} x1; // 只能定义这一次
int main(void) // 主函数
{ student1.num = 10101; // 变量名.成员名 赋值
student1.score = 89.5f; // 给 score 成员赋值
t1.num = 20001; // 方法2的变量用法相同
x1.num = 30001; // 方法3的变量用法相同
printf("%d %d %d\n", student1.num, t1.num, x1.num); // 逐成员输出
printf("%.1f %.1f\n", student1.score, t1.pay); // 浮点成员输出
return 0; // 结束
} // 主函数体结束// == 2. 嵌套结构体 + 初始化 + 两级引用 ==
c
#include <stdio.h> // 引入 printf
struct Date // 先声明日期类型
{ int month; // 月
int day; // 日
int year; // 年
}; // 日期类型结束
struct Student // 再声明学生类型
{ int num; // 学号
char name[20]; // 姓名
char sex; // 性别
int age; // 年龄
struct Date birthday; // 成员本身是结构体
char addr[30]; // 地址
}; // 学生类型结束
int main(void) // 主函数
{ struct Student a = {10101, "Li Lin", 'M', 18, {5, 10, 2006}, "Beijing"}; // 按顺序初始化
struct Student b = {.name = "Zhang Fang"}; // C99 指定初始化
printf("%d %s %c %d\n", a.num, a.name, a.sex, a.age); // 一级成员
printf("%d-%d-%d\n", a.birthday.year, a.birthday.month, a.birthday.day); // 两级成员
printf("%s %d\n", b.name, b.num); // 未指定成员自动置 0
return 0; // 结束
} // 主函数体结束// == 3. 结构体数组(投票统计) ==
c
#include <stdio.h> // 引入 printf
#include <string.h> // 引入 strcmp
struct Person // 声明结构体类型
{ char name[20]; // 候选人姓名
int count; // 得票数
}; // 类型结束
struct Person leader[3] = {{"Li", 0}, {"Zhang", 0}, {"Sun", 0}}; // 数组初始化
int main(void) // 主函数
{ int i; // 循环变量
char vote[20] = "Sun"; // 模拟一张选票
for (i = 0; i < 3; i++) // 遍历 3 位候选人
if (strcmp(vote, leader[i].name) == 0) // 字符串比较用 strcmp
leader[i].count++; // 命中者票数加 1
for (i = 0; i < 3; i++) // 输出结果
printf("%-8s %d\n", leader[i].name, leader[i].count); // 左对齐打印
return 0; // 结束
} // 主函数体结束// == 4. 结构体指针:三种访问写法 + 遍历 + (++p)/(p++) ==
c
#include <stdio.h> // 引入 printf
struct Student // 声明结构体类型
{ int num; // 学号
float score; // 成绩
}; // 类型结束
int main(void) // 主函数
{ struct Student s = {10101, 89.5f}; // 定义并初始化变量
struct Student *p = &s; // p 指向 s
struct Student stu[3] = {{201, 70}, {202, 80}, {203, 90}}; // 结构体数组
printf("%d\n", s.num); // 写法1:变量名.成员
printf("%d\n", (*p).num); // 写法2:(*p).成员,括号不能省
printf("%d\n", p->num); // 写法3:p->成员
p->num = 20202; // 通过指针改成员
printf("%d %d\n", s.num, p->num); // 改的是 s 本身
for (p = stu; p < stu + 3; p++) // p++ 跨过一个元素
printf("%d ", p->num); // 输出当前元素
printf("\n"); // 换行
p = stu; // 回到首元素
printf("%d\n", (p++)->num); // 先取值 201,再后移
printf("%d\n", (++p)->num); // 先前移,再取值 203
return 0; // 结束
} // 主函数体结束// == 5. 结构体作参数:值传递 vs 地址传递 ==
c
#include <stdio.h> // 引入 printf
struct Student // 声明结构体类型
{ int num; // 学号
float score; // 成绩
}; // 类型结束
void changeByValue(struct Student s) // 形参是结构体变量
{ s.score = 100.0f; // 只改副本
} // 函数结束
void changeByPointer(struct Student *p) // 形参是结构体指针
{ p->score = 100.0f; // 改的是原件
} // 函数结束
int main(void) // 主函数
{ struct Student a = {10101, 60.0f}; // 定义并初始化
changeByValue(a); // 传整张表的副本
printf("%.1f\n", a.score); // 输出 60.0,没被改
changeByPointer(&a); // 传地址
printf("%.1f\n", a.score); // 输出 100.0,被改了
return 0; // 结束
} // 主函数体结束// == 6. 共用体:成员共用同一段内存 ==
c
#include <stdio.h> // 引入 printf
union Data // 声明共用体类型
{ int i; // 成员 i,4 字节
char ch; // 成员 ch,1 字节
float f; // 成员 f,4 字节
}; // 类型结束
int main(void) // 主函数
{ union Data a; // 定义共用体变量
printf("%d\n", (int)sizeof(union Data)); // 输出 4:等于最长成员
a.i = 97; // 按 int 存 97
printf("%d %c\n", a.i, a.ch); // 按 int 读 97,按 char 读 'a'
a.ch = 'B'; // 按 char 存 'B',覆盖原内容
printf("%d %c\n", a.i, a.ch); // i 也变成 66
return 0; // 结束
} // 主函数体结束// == 7. 枚举类型 ==
c
#include <stdio.h> // 引入 printf
enum Weekday { sun, mon, tue, wed, thu, fri, sat }; // 默认 0~6
int main(void) // 主函数
{ enum Weekday d = wed; // 定义枚举变量
printf("%d\n", d); // 输出 3
printf("%d %d\n", sun, sat); // 输出 0 和 6
if (d > mon) printf("yes\n"); // 枚举可以比较
switch (d) // 输出单词要自己翻译
{ case sun: printf("Sunday\n"); break; // 0
case mon: printf("Monday\n"); break; // 1
case tue: printf("Tuesday\n"); break; // 2
case wed: printf("Wednesday\n"); break; // 3
case thu: printf("Thursday\n"); break; // 4
case fri: printf("Friday\n"); break; // 5
case sat: printf("Saturday\n"); break; // 6
} // switch 结束
return 0; // 结束
} // 主函数体结束// == 8. typedef:给已有类型起别名 ==
c
#include <stdio.h> // 引入 printf
typedef int Count; // int 的别名 Count
typedef struct { int month, day, year; } Date; // 结构体的别名 Date
typedef int Num[5]; // 5 个 int 的数组的别名 Num
int main(void) // 主函数
{ Count i = 10; // 等价 int i = 10
Date d = {10, 1, 2026}; // 等价 struct {...} d
Num a = {1, 2, 3, 4, 5}; // 等价 int a[5]
printf("%d %d %d\n", i, d.year, a[4]); // 输出 10 2026 5
printf("%d\n", (int)sizeof(Num)); // 输出 20
return 0; // 结束
} // 主函数体结束// == 9. 静态链表(选学) ==
c
#include <stdio.h> // 引入 printf
struct Student // 声明结构体类型
{ int num; // 数据域:学号
float score; // 数据域:成绩
struct Student *next; // 指针域:下一个结点
}; // 类型结束
int main(void) // 主函数
{ struct Student a = {10101, 89.5f, NULL}; // 第 1 个结点
struct Student b = {10103, 90.0f, NULL}; // 第 2 个结点
struct Student c = {10107, 85.0f, NULL}; // 第 3 个结点
struct Student *head, *p; // 头指针和遍历指针
head = &a; // 头指针指向第 1 个
a.next = &b; // 1 指向 2
b.next = &c; // 2 指向 3
c.next = NULL; // 表尾置空
for (p = head; p != NULL; p = p->next) // 从头走到尾
printf("%d %.1f\n", p->num, p->score); // 输出当前结点
return 0; // 结束
} // 主函数体结束二、课后题 · 代码题
// == 习题 1 + 2:days 函数算某日是本年第几天 ==
c
#include <stdio.h> // 引入 printf、scanf
struct Date // 声明日期类型
{ int year; // 年
int month; // 月
int day; // 日
}; // 类型结束
int days(struct Date d) // 结构体变量作形参
{ int tab[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 下标即月份
int i, sum = 0; // i 循环,sum 累计
if ((d.year % 4 == 0 && d.year % 100 != 0) || d.year % 400 == 0) // 闰年判断
tab[2] = 29; // 2 月改 29 天
for (i = 1; i < d.month; i++) // 累加前面整月
sum += tab[i]; // 加上第 i 月天数
return sum + d.day; // 再加当月日
} // 函数结束
int main(void) // 主函数
{ struct Date d; // 定义变量
printf("请输入年 月 日:"); // 提示
scanf("%d %d %d", &d.year, &d.month, &d.day); // 读三个成员
printf("第 %d 天\n", days(d)); // 传整张表并输出
return 0; // 结束
} // 主函数体结束// == 习题 3 + 4:input 输入、print 输出 5 个学生 ==
c
#include <stdio.h> // 引入 printf、scanf
#define N 5 // 学生人数
struct Student // 声明结构体类型
{ int num; // 学号
char name[20]; // 姓名
float score[3]; // 3 门成绩
}; // 类型结束
void input(struct Student stu[]) // 形参是结构体数组
{ int i; // 循环变量
for (i = 0; i < N; i++) // 逐个读入
scanf("%d %s %f %f %f", &stu[i].num, stu[i].name, &stu[i].score[0], &stu[i].score[1], &stu[i].score[2]); // 姓名不取地址
} // 函数结束
void print(struct Student stu[]) // 形参也是结构体数组
{ int i; // 循环变量
for (i = 0; i < N; i++) // 逐个输出
printf("%-8d%-12s%-8.1f%-8.1f%-8.1f\n", stu[i].num, stu[i].name, stu[i].score[0], stu[i].score[1], stu[i].score[2]); // 左对齐
} // 函数结束
int main(void) // 主函数
{ struct Student stu[N]; // 结构体数组
input(stu); // 数组名即首地址
print(stu); // 输出同一份数据
return 0; // 结束
} // 主函数体结束// == 习题 5:10 个学生求总平均分和最高分学生 ==
c
#include <stdio.h> // 引入 printf、scanf
#define N 10 // 学生人数
struct Student // 声明结构体类型
{ int num; // 学号
char name[20]; // 姓名
float score[3]; // 3 门成绩
float aver; // 平均分
}; // 类型结束
int main(void) // 主函数
{ struct Student stu[N], max; // 数组 + 最高分学生
int i; // 循环变量
float sum = 0; // 平均分总和
for (i = 0; i < N; i++) // 逐个读入
{ scanf("%d %s %f %f %f", &stu[i].num, stu[i].name, &stu[i].score[0], &stu[i].score[1], &stu[i].score[2]); // 读 5 项
stu[i].aver = (stu[i].score[0] + stu[i].score[1] + stu[i].score[2]) / 3.0f; // 算平均分
sum += stu[i].aver; // 累加
} // 循环结束
max = stu[0]; // 假设第 0 个最高
for (i = 1; i < N; i++) // 从第 1 个比较
if (stu[i].aver > max.aver) max = stu[i]; // 整张表复制
printf("总平均成绩:%.2f\n", sum / N); // 输出总平均
printf("最高分:%d %s %.1f %.1f %.1f 平均%.2f\n", max.num, max.name, max.score[0], max.score[1], max.score[2], max.aver); // 输出最高分学生
return 0; // 结束
} // 主函数体结束习题 6~12 都是链表题(本章 9.4 节为选学),完整代码见《第10课_结构体.md》第四节的习题 6~12,那里逐题都有。
三、我出的题 · 随课练习
🔮 猜输出
c
#include <stdio.h> // 引入 printf
struct Student { int num; char name[10]; }; // 声明结构体类型
int main(void) // 主函数
{ struct Student a = {101, "Li"}, b; // a 初始化,b 未初始化
b = a; // 结构体整体赋值
b.num = 202; // 只改 b 的 num
printf("%d %s\n", a.num, a.name); // 输出 a 的两个成员
return 0; // 结束
} // 主函数体结束c
#include <stdio.h> // 引入 printf
struct Point { int x, y; }; // 声明结构体类型
int main(void) // 主函数
{ struct Point a = {1, 2}, *p = &a; // a 初始化,p 指向 a
p->x = 10; // 通过指针改 x
(*p).y = 20; // 通过 (*p) 改 y
printf("%d %d\n", a.x, a.y); // 输出 a 的成员
return 0; // 结束
} // 主函数体结束c
#include <stdio.h> // 引入 printf
struct S { int num; }; // 声明结构体类型
int main(void) // 主函数
{ struct S s[3] = {{1}, {2}, {3}}; // 结构体数组
struct S *p = s; // p 指向首元素
printf("%d ", (p++)->num); // 先取值再后移
printf("%d ", p->num); // 输出当前成员
printf("%d\n", (++p)->num); // 先前移再取值
return 0; // 结束
} // 主函数体结束c
#include <stdio.h> // 引入 printf
union U { int i; char c; }; // 声明共用体类型
int main(void) // 主函数
{ union U u; // 定义共用体变量
u.i = 65; // 按 int 存 65
printf("%d %c\n", u.i, u.c); // 两种类型各读一次
return 0; // 结束
} // 主函数体结束🛠 变式题
// 变式 1(照猫画虎):补全程序,输出价格最高的书
c
// 在 VS 里补全三个空后运行
struct Book
{ int id; // 书号
char name[20]; // 书名
float price; // 价格
};
// (1) 初始化 3 本书
// (2) 找出 price 最大的元素,把下标存进 k
// (3) 输出那个元素的书号、书名、价格
// 想一想:能不能直接 printf("%s", b[0])?为什么?// 变式 2(改需求):把例 9.4 改成从低到高排序,并输出名次
c
// 原程序关键两行:
// if (stu[j].score > stu[k].score) k = j;
// temp = stu[k]; stu[k] = stu[i]; stu[i] = temp;
// 请把比较方向反过来,并在 printf 里多加一列名次 i+1// 变式 3(补全):用结构体指针作参数、返回结构体类型的 max 函数
c
#define N 3
struct Student
{ int num; // 学号
char name[20]; // 姓名
float score[3]; // 3 门成绩
float aver; // 平均分
};
// 请写:struct Student max(struct Student *p)
// 要求:在 p[0] 到 p[N-1] 中找 aver 最大的元素,用 return 返回整张表
// 主函数调用:struct Student m = max(stu); 预期输出 3 Sun 95.00🐛 改错题
c
// ❌ 错误示例(故意)
#include <stdio.h>
struct Student
{ int num;
char name[20];
} // 错 1:末尾少分号,应为 };
int main(void)
{ Student s = {1, "Li"}; // 错 2:类型名要写 struct Student
printf("%d %s\n", s.num, s.name);
return 0;
}c
// ❌ 错误示例(故意)
#include <stdio.h>
struct Student
{ int num;
char name[20];
};
int main(void)
{ struct Student s = {1, "Li"};
printf("%s\n", s); // 错:不能整体输出结构体,只能逐成员
return 0;
}c
// ❌ 错误示例(故意)
#include <stdio.h>
struct Student
{ int num;
char name[20];
};
int main(void)
{ struct Student s = {1, "Li"}, *p = &s;
printf("%d\n", *p.num); // 错:. 优先级高于 *,应写 (*p).num 或 p->num
return 0;
}四、加练题
(暂无。想加练就说"再出几道 XX 题",我出的题会标上日期追加在这块。)