CC语言通关
第10课 · 要写代码
教材对照
第10课配套 · 纯代码

第10课 · 要写代码

结构体变量/数组/指针、链表、共用体、枚举、typedef

22 段代码每行都有注释边敲边想
教材依据第9章《用户自己建立数据类型》书页 p.293–330
本课覆盖:教材第9章正文 + 书末习题(p.330)。标「教材 p.X」= 教材段落总结;「例题」= 教材例题;「习题」= 书末题目;「本课补充」= 非教材原文。
在全书对照表中查看 →

纯代码 + 逐行注释。每段都是完整可编译程序,复制进 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 题",我出的题会标上日期追加在这块。)