第五部分 · 编程语言基础 —— C 语言

第七章 结构体、共用体
及链表

考点精讲 · 典型考题 · 答案与解析一键展开
教材 P193–P197 原页完整还原,讲练一体,可直接投屏授课
链表操作:近三年 20–25 分结构体:2024 / 2025 各 5 分题型:分析结果题 · 改错题 · 设计题含 27 道配套习题
职教高考 · 计算机应用类专业综合知识(下)
课件目录

全章脉络一览

第七章 · 结构体、共用体及链表

共 74 页 · 点击任意板块可直接跳转;也可按 M 打开右侧完整目录。

01

考点解读

先看清考试怎么考:考纲要求、近年分值分布、高频考点与重难点,把有限的复习时间压在最值钱的地方。

考纲:结构体、共用体的概念及链表的操作链表是绝对主角
考点解读 · 考纲原文

考纲要求与考点解读

第七章 · 结构体、共用体及链表

考纲要求:结构体、共用体的概念及链表的操作。

考试以 程序分析结果题、程序改错题、程序设计题 三种题型呈现,全部是“读代码 / 改代码 / 写代码”的能力考查。

概念层

关键词一 · 概念

  • 结构体与共用体的定义形式
  • 成员的三种引用方式
  • struct 与 union 内存分配的区别
  • typedef 起别名的规则

操作层(分值主战场)

关键词二 · 操作

  • 链表的建立(创建)
  • 链表的插入
  • 链表的删除
  • 链表的反序、输出
一句话定位:概念题基本上只考选择题和填空题;真正的分数在链表的五大操作上——它同时是分析结果题、改错题、设计题的主角。
考点解读 · 原表对照

近三年考点、题型、分值列表

第七章 · 结构体、共用体及链表
教材原页

可否读化重排(左为教材原表 P193)

考 点题 型2023 年2024 年2025 年
结构体的定义及应用程序分析结果题、程序改错题、程序设计题—5 分5 分
共用体的定义及应用程序分析结果题、程序改错题、程序设计题———
链表的操作程序分析结果题、程序改错题、程序设计题25 分20 分20 分
typedef程序分析结果题、程序改错题、程序设计题———

左图为试卷原页表格切片,完整保留原始排版与表格线;本章考点历年题型完全一致,仅为分值不同。

考点解读 · 重点提示

高频考点与重难点分析

第七章 · 结构体、共用体及链表

高频考点:结构体、共用体及链表的操作
(创建、删除、插入、反序、输出)

重难点分析:链表的操作

难点不在语法,而在指针的操作顺序。

五大操作分解

  1. 链表的创建 —— 动态申请结点(malloc),首结点 / 尾插法建表;
  2. 链表的插入 —— 指针顺序不能颠倒:先接后继,再断前驱;
  3. 链表的删除 —— 先摘链再 free;删首结点要改头指针;
  4. 链表的反序 —— 三指针就地逆置:记后继、掉箭头、双前移;
  5. 链表的输出 —— 用临时指针遍历,不要改动头指针。
分值提示:链表近三年分别考了 25 分、20 分、20 分,几乎每年必出一道主观大题。建议复习精力分配:链表 60% + 结构体 30% + 共用体与 typedef 10%。
02

知识点 1 结构体

最基础的构造数据类型,四个问题一次讲透:类型怎么声明?变量怎么定义?成员怎么引用?初始化怎么写?

声明类型 → 定义变量 → 引用成员 → 初始化
知识点精讲 · 结构体

概述:为什么需要结构体

第七章 · 结构体、共用体及链表

数组 = 同类型

数组:具有相同数据类型的数据序列。

各元素类型必须一致,适合表示“一批同类数据”,但无法表示一条记录中的多种属性。

结构体 = 不同数据类型

结构体:不同数据类型的数据序列。

把一个对象的多种属性“打包”在一起,适合表示一条完整记录。

这两种“数据”是不同的:数组是数据元素,结构体是数据域。
结构体是一种构造数据类型 —— 即先要定义结构体类型,然后定义其结构体变量。

例如,一个学生的“学号 + 姓名 + 性别 + 成绩”是 4 个不同属性,用数组无法表示,用结构体就非常自然:

C一条记录 = 一个结构体类型
知识点精讲 · 结构体

声明结构体类型的一般形式

第七章 · 结构体、共用体及链表

教材 P193 一般形式

struct 结构体名
{ 成员表列 };

四点要点

  • struct 是关键字,声明结构体类型时必须写;
  • “结构体名”遵循标识符规则(字母、数字、下划线,不能以数字开头);
  • “成员表列”是对成员进行声明,与变量的声明方法一样;
  • 花括号外必须以分号结尾 —— 这是最常见的改错点!
C教材 P193 原例
记忆口诀:结构体类型只是“图纸”,声明类型时不分配内存;只有定义了结构体变量,系统才真正为其分配存储空间。
知识点精讲 · 结构体

定义结构体变量 · 方法一

第七章 · 结构体、共用体及链表

思路

先定义结构体类型,再声明结构体变量。这是最常用、最规范的一种写法。

C教材 P194 原例

扩展:用宏定义简化类型名

声明了两个变量 a1 和 a2 为 struct stu 结构类型。也可以用宏定义使一个符号常量来表示一个结构类型:

C
知识点精讲 · 结构体

定义结构体变量 · 方法二

第七章 · 结构体、共用体及链表

思路

在定义结构体类型的同时,声明结构体变量。类型和变量写在一起,一步到位。

C教材 P194–P195 原例

优点:写法紧凑,定义与使用靠近,可读性好。

注意:此处 a1、a2 声明在结构体类型之后,因此该类型是匿名的 —— 后面无法再定义同类型的其他变量。

辨析:结构体类型是“图纸”,结构体变量是“按图纸造出来的实物”。方法二省掉了重复书写 struct stu 的麻烦。
知识点精讲 · 结构体

定义结构体变量 · 方法三

第七章 · 结构体、共用体及链表

思路

直接声明结构体变量。连结构体名都省去,直接给出变量名。

C教材 P195 原例

三个限制

  • 这种写法也叫无名结构体;
  • 因为没有结构体名,不能再用它定义别的变量;
  • 也不能把这个类型用作函数参数类型;
  • 只能使用这一次,适合临时、一次性定义的场合。
三种方法只有“类型能不能复用”的区别,成员引用方式完全相同。
知识点精讲 · 结构体

三种定义方法对比

第七章 · 结构体、共用体及链表
方 法写 法特 点考试中的出现形式
方法一先 struct 类型,再 struct 类型 变量名;最规范,类型可复用;用宏定义还可进一步简化改错题 / 设计题最常用
方法二定义类型的同时,在 } 后写变量名紧凑、一步到位;但类型匿名,无法复用程序分析结果题中常见
方法三struct { … } 变量名;完全匿名,只此一次偶见于分析结果题

必背提醒

  • 无论哪种写法,成员表列之后、花括号之外的分号都不能少;
  • 三者的成员引用方式完全一样:变量名.成员名;
  • 方法二、方法三属于匿名结构体,除了定义时给出的变量,不能再定义其他变量。
知识点精讲 · 结构体

结构体变量成员的引用 · 三种方式

第七章 · 结构体、共用体及链表

三种引用方式

(1)结构体变量名.成员名   → a.num
(2)(*指针变量名).成员名  → (*p).num
(3)指针变量名->成员名  → p->num

C教材 P195 原例

要引用 a 中的 num 成员,以下三种写法完全等价:

a.num  (*p).num  p->num

推荐:变量用 .,指针用 -> —— p->num 最简洁、最不容易写错。

易错:(*p).num 的括号不能省!写成 *p.num 会被理解为 *(p.num),编译报错。
知识点精讲 · 结构体

成员引用注意事项 ① — ④

第七章 · 结构体、共用体及链表

① 优先级最高

① “.” 和 “->” 运算符的优先级最高。

因此 x.a++ 是 (x.a)++,而不是 x.(a++)。

② 成员可单独使用

② 对结构体中的成员(即“域”),可以单独使用,它的作用与地位相当于普通变量。

可以 a.num = 100;、a.num++、&a.num,和普通 int 变量用法完全一样。

③ 成员可以是结构体

③ 成员可以是一个结构体变量。

这就是结构体的嵌套,引用时要逐级写成员运算符(详见 ⑥)。

④ 成员名可同名

④ 成员名可以与程序中的变量名相同,但二者不代表同一对象。

例如程序中有一个变量 num,结构体中也有成员 num,两者互不干扰,各占各的内存。

知识点精讲 · 结构体

成员引用注意事项 ⑤ — ⑥

第七章 · 结构体、共用体及链表

⑤ 不能整体输入输出

⑤ 不能将结构体变量作为一个整体进行输入和输出。

例如:已定义 a1 和 a2 为结构体变量,并且它们已有值。

C

正确做法:逐个成员输出 ——
printf("%d,%s\n", a1.num, a1.name);

⑥ 嵌套结构体逐级引用

⑥ 如果成员本身属于一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员。只能对最低级的成员进行赋值、存取和运算。

C教材 P196 原例
知识点精讲 · 结构体

成员引用注意事项 ⑦ — ⑧

第七章 · 结构体、共用体及链表

⑦ 成员可参与运算

⑦ 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。

C教材 P196 原例

整型成员可做 + - * / % ++ -- 等运算;字符数组成员则受数组限制,不能整体赋值。

⑧ 取成员地址 / 取变量地址

⑧ 可以引用结构体变量成员的地址,也可以引用结构体变量的地址。

C教材 P196–P197 原例

结构体变量的地址主要用作函数参数或结合结构体指针,用来传递结构体变量的地址。例如:

C
知识点精讲 · 结构体

结构体变量的初始化

第七章 · 结构体、共用体及链表

整体初始化

可以在定义结构体变量时对其赋初值。初值用花括号括起,按成员在结构体中的顺序一一对应。

C教材 P197 原例

初始化的三条规则

  • 初值个数可以少于成员个数,缺省的成员自动取 0(数值型)或 '\0'(字符型);
  • 初值个数不能多于成员个数,否则编译报错;
  • 字符数组成员用字符串常量初始化:char c[10] = "abc";。
知识点精讲 · 结构体

定义后再赋值:两种方式

第七章 · 结构体、共用体及链表

scanf 输入

方式一:用 scanf 逐项输入

C教材 P197 原例

注意:x.c 是数组名,前面不加 &。

逐成员赋值

方式二:逐个成员赋值

C教材 P197 原例

关键:x.c = "abc"; 是错误的写法。

必考易错点:字符数组名是地址常量,不能作为赋值号的左值。字符串赋值必须使用 strcpy(目标, 源)(需 #include <string.h>)。要包含所需的头文件,这一点在程序改错题中出现频率极高。
知识点精讲 · 结构体

本节小结与自测

第七章 · 结构体、共用体及链表

知识清单

  • 结构体是不同数据类型的数据序列,属构造数据类型;
  • 声明类型:struct 名 { 成员表列 }; 分号不能少;
  • 定义变量三种方法:先类型后变量 / 同时 / 直接(后两种为匿名);
  • 成员引用三种形式:.、(*p).、->;
  • 不能整体输入输出,只能按成员操作;
  • 初始化可整体赋初值,定义后赋值需逐成员或用 strcpy。

课堂自测

请判断下列写法是否正确:

  1. struct stu { int a; }(无分号)
  2. printf("%d", a);(a 为结构体变量)
  3. a.name = "Li";(name 为字符数组)
  4. p.a(p 为结构体指针)

全部错误

  1. 结构体定义末尾缺少分号,应写 };;
  2. 结构体变量不能整体输出,应逐个成员输出;
  3. 字符数组不能整体赋值,应用 strcpy(a.name, "Li");;
  4. 指针访问成员要用 p->a 或 (*p).a。

这四处正好对应本章改错题的四大高频考点:缺分号、整体输入输出、数组整体赋值、指针访问成员符号用错。把这四句话背下来,改错题至少能拿一半分。

03

知识点 2 共用体

与结构体形式相似、内存模型却完全不同:成员共享同一段空间,同一时刻只有一个成员有效。

union考纲:掌握共用体的概念
知识点精讲 · 共用体

共用体的定义与内存特点

第七章 · 结构体、共用体及链表

一般形式

union 共用体名
{ 成员表列 };

C共用体的典型定义

四条核心性质

  • 所有成员共用同一段内存,起始地址完全相同;
  • 共用体变量所占内存长度 = 最长成员所占的字节数(并满足对齐要求);
  • 同一时刻只有最后被赋值的成员有效,赋新值会覆盖旧值;
  • 成员的引用方式与结构体完全相同:a.i、(*p).i、p->i。
典型用途:让几个不同类型的数据共享一段空间以节省内存;或“按不同格式查看同一段数据”(例如把一个 4 字节整数当作 4 个字符来读)。
知识点精讲 · 共用体

共用体 与 结构体 对比

第七章 · 结构体、共用体及链表
对比项struct 结构体union 共用体
内存占用各成员各占独立空间,总长 ≥ 各成员之和所有成员共用一段空间,长度 = 最长成员
成员地址各成员地址互不相同所有成员首地址相同
赋值影响修改一个成员不影响其他成员修改一个成员会覆盖其他成员
有效成员所有成员同时有效只有最后被赋值的那个成员有效
引用方式完全一样:. 和 ->完全一样:. 和 ->

相同点

  • 定义形式(仅关键字不同)
  • 成员的引用方式
  • 初始化写法(只初始化第一个成员)

考查方式

  • 问 sizeof 的值
  • 问成员是否互相影响
  • 与结构体对比选择

4

有定义 union un { int a; char b; float c; };,则 sizeof(union un) 是 9 吗?

C

不是 9,而是 4。共用体长度取最长成员:int 4、char 1、float 4 —— 最长是 4 字节,而不是 4 + 1 + 4 = 9。

对照记忆:

  • struct { int a; char b; float c; } → 各成员相加并考虑对齐,通常是 12;
  • union { int a; char b; float c; } → 取最长成员,即 4。
04

知识点 3 链表

全章最重要的 20–25 分:创建、插入、删除、反序、输出五大操作。抓住“指针顺序”这个牛鼻子,题目就变成填空题。

近三年 25 / 20 / 20 分重难点程序设计题必考
知识点精讲 · 链表

链表的结点结构与类型定义

第七章 · 结构体、共用体及链表

什么是链表

链表:用一组任意的存储单元存放线性表的元素,每个元素除了存数据,还要存下一个元素的地址。

因此每个元素称为一个结点,结点 = 数据域 + 指针域。

两个关键概念

头指针 head:指向第一个结点,是链表的“入口”,绝不能丢;

表尾:最后一个结点的 next 必须为 NULL,这是遍历结束的标志。

C结点类型定义(教材与考试的标准写法)
为什么指针域写成 struct node *next?因为在定义这个类型的过程中,类型别名 Node 尚未生效,C 语言规定此时必须使用 struct node * 这种完整写法,不能直接写 Node *next;。

建立链表的两种方式

  • 头插法 —— 新结点插在表头,建立后数据顺序与输入顺序相反;
  • 尾插法 —— 新结点接在表尾,建立后数据顺序与输入顺序一致(本课件采用尾插法)。
知识点精讲 · 链表

操作一:建立链表(尾插法)

第七章 · 结构体、共用体及链表
C尾插法完整实现

套路

三步走:

  1. 申请空间:p = (LinkList)malloc(sizeof(Node));
  2. 填数据域、指针域置空:p->data = x; p->next = NULL;
  3. 接到表尾并更新尾指针:tail->next = p; tail = p;
易错点:① 忘记给 p->next 置 NULL → 遍历时越界或死循环;② 只用 head 逐个找表尾 → 时间复杂度退化成 O(n²);③ 直接移动 head → 链表入口丢失,函数外再也访问不到这条链。
知识点精讲 · 链表

操作二:遍历与输出

第七章 · 结构体、共用体及链表
C遍历输出标准写法

套路

三句口诀:

  1. 用临时指针 p 从 head 出发 —— 绝不能直接改 head;
  2. 循环条件是 p != NULL(不是 p->next != NULL);
  3. 先访问结点,再执行 p = p->next 后移。

错误示范

典型错误一:写成 head = head->next;,头指针被一步步“吃掉”,函数返回后链表再也无法访问。

错误示范

典型错误二:循环条件写成 while (p->next != NULL),则最后一个结点不会被输出,结果少打印一个数。

知识点精讲 · 链表

操作三:插入结点

第七章 · 结构体、共用体及链表
C插入的两步操作

套路

顺序是命门:

  1. 先接后继:s->next = p->next;
  2. 再断前驱:p->next = s;
为什么不能颠倒?若先写 p->next = s;,p 原来后继结点的地址就被覆盖丢失了,s 的 next 再也接不上原链表 —— 链表断裂,丢失后面所有结点。

延伸理解

单链表的局限:要找 p 的前驱必须从头遍历(无法 O(1) 前插)。若题目要求频繁前插,可考虑双向链表,或在遍历时额外记录前驱指针 pre。

知识点精讲 · 链表

操作四:删除结点

第七章 · 结构体、共用体及链表
C删除的两步 + 头结点特例

套路

口诀:先摘链,再释放。

  1. q = p->next; 用 q 记住要删的结点;
  2. p->next = q->next; 让 p 跨过 q;
  3. free(q); 归还不用的空间。

三种典型错误

  • 只摘链、不 free → 内存泄漏;
  • 先 free、后摘链 → q->next 成为野指针,结果错误;
  • 删除首结点后忘记移动 head → 头指针悬空。

分类讨论

  • 删第一个结点 —— 必须修改 head;
  • 删中间结点 —— 前驱的 next 指向后继;
  • 删最后一个结点 —— 前驱的 next 置为 NULL。
知识点精讲 · 链表

操作五:反序(就地逆置)

第七章 · 结构体、共用体及链表
C三指针就地逆置

套路

四步口诀:

  1. 记后继 r = p->next;
  2. 掉箭头 p->next = q;
  3. 双前移 q = p; p = r;

扩展写法

另一种思路:头插法重建。依次取出原表的每个结点,把它头插到一个新表中,结果自然就是反序的。

两种方法效果相同;就地逆置不额外申请空间,考试通常要求写就地逆置。

最易错:逆置后新表头是原表尾,必须写成 head = Reverse(head);。若只调用 Reverse(head); 而不接收返回值,head 仍指向原表头(此时已是表尾),链表只剩一个结点可用。
知识点精讲 · 链表

链表操作 · 易错点速查

第七章 · 结构体、共用体及链表

八大易错点

  • 申请结点后忘记 p->next = NULL;
  • malloc 返回值未检查、未强制类型转换;
  • 遍历时直接改动 head,链表入口丢失;
  • 循环条件写成 p->next != NULL,漏输出最后一个结点;
  • 插入两步顺序颠倒,链表断裂;
  • 删除时只摘链不 free,造成内存泄漏;
  • free 后未置 NULL,形成野指针;
  • 逆置后忘记用返回值更新 head。

四句话模板(背下来就能默写)

C
考试技巧:程序分析结果题给出的是完整链表代码,只要在草稿纸上画出“方框 + 箭头”,把指针指向一步步画出来,插入 / 删除 / 逆置的题目都不会做错。
05

知识点 4 typedef

给已有类型起个别名,让结构体、链表的写法瞬间变短,是链表代码中出场率最高的“配角”。

typedef
知识点精讲 · typedef

typedef:为已有类型起别名

第七章 · 结构体、共用体及链表

一般形式

typedef 已有类型 新类型名;

C结构体的 typedef 写法

四条要点

  • typedef 是给已有类型起别名,不会产生新的数据类型;
  • 起了别名之后,原来的类型名仍然可用(这点与 #define 不同);
  • typedef 语句以分号结尾,它“变量名”的位置写的就是类型别名;
  • typedef 处理的是类型,不分配内存,在编译期完成替换。
典型辨析:注意区分下面两句 ——
typedef struct node { … } Node; Node 是类型名;
struct node { … } n; n 是变量名。
区别仅在于前面有没有 typedef —— 这也是常考的辨析点。
06

原卷对照

教材第 193–197 页原页扫描件,完整保留原始排版,随时可与前面的重排讲解页对照核验。

5 页原卷可点击放大
原卷对照 · 第 193 页

章节导入 · 考点解读 · 结构体概述与类型声明

第七章 · 结构体、共用体及链表
教材原页

本页要点(左图可点击放大)

  • 考纲解读:结构体、共用体的概念及链表的操作。
  • 近三年考点、题型、分值列表(完整表格)。
  • 高频考点 / 重难点分析:链表的操作。
  • 知识点 1 结构体:概述、声明结构体类型的一般形式。

本页内容已在上文“知识点精讲”中完整重排讲解,此处保留原卷原貌,便于课堂对照核验。

原卷对照 · 第 194 页

定义结构体变量 · 方法(一)(二)

第七章 · 结构体、共用体及链表
教材原页

本页要点(左图可点击放大)

  • struct stu { int num; char name[10]; }; 完整示例。
  • 方法(1)先定义结构体类型,再声明结构体变量。
  • 用 #define STU struct stu 宏定义简化类型名。
  • 方法(2)在定义结构体类型的同时,声明结构体变量。

本页内容已在上文“知识点精讲”中完整重排讲解,此处保留原卷原貌,便于课堂对照核验。

原卷对照 · 第 195 页

方法(二)(三)· 成员引用的三种方式

第七章 · 结构体、共用体及链表
教材原页

本页要点(左图可点击放大)

  • 方法(2)续:在 } 之后声明 a1, a2。
  • 方法(3)直接声明结构体变量(匿名结构体)。
  • 结构体变量成员引用的三种方式。
  • struct { … } a, *p; 与 p = &a; 的完整示例。

本页内容已在上文“知识点精讲”中完整重排讲解,此处保留原卷原貌,便于课堂对照核验。

原卷对照 · 第 196 页

成员引用注意事项 ① — ⑧

第七章 · 结构体、共用体及链表
教材原页

本页要点(左图可点击放大)

  • (*p).num、p->num 的等价写法。
  • 注意 ①—④:优先级最高、成员可单独使用、成员可嵌套、成员名可同名。
  • 注意 ⑤—⑥:不能整体输入输出;嵌套结构体逐级引用。
  • 注意 ⑦—⑧:成员可参与运算、可引用成员与变量的地址。

本页内容已在上文“知识点精讲”中完整重排讲解,此处保留原卷原貌,便于课堂对照核验。

原卷对照 · 第 197 页

注意事项(续)· 结构体变量的初始化

第七章 · 结构体、共用体及链表
教材原页

本页要点(左图可点击放大)

  • 不能整体读入结构体变量:scanf("%d%d", &x); 是错误的。
  • 结构体变量的地址用于函数参数或结合结构体指针。
  • 结构体变量的初始化:x = {1, 'F', "abc"};
  • 定义后赋值:scanf 逐项输入,或逐成员赋值 + strcpy。

本页内容已在上文“知识点精讲”中完整重排讲解,此处保留原卷原貌,便于课堂对照核验。

07

典型考题精讲

按考点表中的三种题型,加上选择题与填空题,共 27 题,每题一页:先做题,再点开答案与解析。

选择题 8填空题 6分析结果题 5改错题 4设计题 4
典型考题 · 导读

题型分布与使用方法

第七章 · 结构体、共用体及链表

共 27 题

题 型题 量主要考查点
选择题8 题概念辨析、sizeof、指针符号
填空题6 题术语记忆、关键代码片段
程序分析结果题5 题读代码算结果
程序改错题4 题缺分号 / 数组赋值 / 指针顺序
程序设计题4 题结构体统计 → 链表建表与逆置

课堂互动方式

  1. 先让学生独立作答或上台口述思路;
  2. 再点击“答案 / 解析”核验;
  3. 按键 A 可一键展开或收起本页答案;
  4. 底部导航可随时跳转题目,也可按 M 打开目录。
建议课堂节奏:选择题与填空题快问快答;程序分析结果题让学生先画内存图;改错题要求写出“错误 + 原因 + 改正”;程序设计题至少完整板书一道链表建表与一道链表逆置。
典型考题 · 选择题

选择题(共 8 题)

第七章 · 结构体、共用体及链表

本组题覆盖:结构体的定义与成员引用、sizeof 的计算、共用体的内存特点、typedef 的规则,以及链表插入与删除的核心指针操作。

每题一页,答案与解析默认折叠,课堂上先让学生举手作答,再点开核验。

  • 考点分布:链表 2 题、结构体 3 题、共用体 2 题、typedef 1 题
  • 操作提示:按 A 键一键展开 / 收起本页答案;按 → 翻下一页
选择题第 1 题结构体 · 类型与变量
下列关于结构体类型的叙述中,正确的是(  )
A. 结构体中各成员的数据类型必须相同
B. 定义结构体类型时必须同时定义结构体变量
C. 结构体是构造数据类型,各成员的数据类型可以不同✓
D. 结构体变量所占的内存字节数等于各成员所占字节数之和

C

结构体是“不同数据类型”的集合,各成员类型可以互不相同。

  • A 错:这正是结构体区别于数组的关键 —— 各成员类型可以完全不同;
  • B 错:可以只声明类型,之后再用 struct 类型名 变量名; 定义变量;
  • C 对:结构体属于构造数据类型;
  • D 错:受内存对齐影响,结构体变量长度通常 ≥ 各成员长度之和,并不一定相等。
选择题第 2 题结构体 · 成员引用
已有定义 struct stu a, *p; 且 p = &a;,不能正确引用成员 num 的是(  )
A. a.num
B. (*p).num
C. p->num
D. a->num✓

D

-> 只能作用于指针,a 是结构体变量而不是指针。

  • A 正确:变量用 .;
  • B 正确:*p 就是 a,再加圆括号即可;
  • C 正确:指针用 ->;
  • D 错误:要这么写只能用 (&a)->num,不能写 a->num。
选择题第 3 题结构体 · sizeof 与内存对齐
已知 struct node { int a; char b; float c; } x;(int 占 4 字节、char 占 1 字节、float 占 4 字节,按 4 字节对齐),则 sizeof(x) 的值通常是(  )
A. 7
B. 9
C. 12✓
D. 16

C → 12

按成员顺序计算偏移量:

成 员类 型占用字节偏 移 量
aint40 ~ 3
bchar14
对齐填充—35 ~ 7
cfloat48 ~ 11

合计 12 字节。这也正好说明:结构体长度 ≠ 各成员长度之和(7),因为编译器会为了满足对齐要求插入填充字节。

选择题第 4 题共用体 · 概念辨析
关于共用体(union),下列说法正确的是(  )
A. 共用体的各成员分别占用不同的存储单元
B. 共用体变量所占内存长度等于各成员长度之和
C. 共用体变量所占内存长度等于最长成员的长度✓
D. 共用体变量的所有成员可以同时有效

C

共用体的所有成员共用同一段内存,长度取最长成员。

  • A 错:所有成员起始地址相同,共用同一段空间;
  • B 错:不是相加,而是取最长;
  • C 对:union { int a; char b; float c; } 的长度是 4;
  • D 错:同一时刻只有最后被赋值的那个成员有效,赋新值会覆盖旧值。
选择题第 5 题共用体 · sizeof 计算
有定义 union un { char c; int i; double d; } u;(char 1、int 4、double 8 字节),则 sizeof(u) 的值是(  )
A. 1
B. 4
C. 8✓
D. 13

C → 8

共用体取最长成员:char 1 字节、int 4 字节、double 8 字节 → 最长是 double 的 8 字节,同时 8 也满足所有成员的对齐要求,所以结果是 8。

注意与结构体区分:同样三个成员放在 struct 中,长度会是 16(1 + 填充 3 + 4 + 8)。

选择题第 6 题typedef 规则
下列关于 typedef 的叙述中,错误的是(  )
A. typedef 是给已有类型起别名,并不会产生新的数据类型
B. 执行 typedef struct node { int data; struct node *next; } Node; 后,可用 Node 定义变量
C. 用了 typedef 起别名后,原来的类型名 struct node 就不能再使用了✓
D. typedef 语句以分号结尾

C

typedef 只是建立“别名”,原类型名依然有效,两者可以混用。

  • A 对:typedef 不创建新类型,只起“小名”;
  • B 对:Node a; 等价于 struct node a;;
  • C 错:struct node 与 Node 指的是同一个类型,两者都可以使用;
  • D 对:typedef 是一条完整语句,必须加分号。
选择题第 7 题链表 · 删除结点
在单向链表中,指针 p 指向某结点(其后继结点存在),要删除 p 所指结点的后继结点,正确的语句组是(  )
A. q = p->next; p->next = q->next; free(q);✓
B. p->next = p->next->next;
C. q = p; p = p->next; free(q);
D. free(p->next);

A

C
  • A 对:摘链 + 释放,完整正确;
  • B:指针虽然接对了,但没有 free,被删结点空间无法回收 → 内存泄漏;
  • C:只移动了 p 本身,没有修改前驱指针,链表结构未被改变;
  • D:free(p->next) 之后 p->next 成为野指针,链表断裂。
选择题第 8 题链表 · 插入结点
设指针 p 指向单向链表中某结点,s 指向待插入的新结点(s 已申请到空间)。要把 s 插入到 p 之后,正确的操作是(  )
A. s->next = p->next; p->next = s;✓
B. p->next = s; s->next = p->next;
C. s->next = p; p->next = s;
D. p->next = s->next; s->next = p;

A

C
  • 顺序是命门:必须先让 s 记住 p 原来的后继,再让 p 指向 s;
  • B 顺序颠倒:执行 p->next = s; 后,p 的原后继地址已经丢失,s->next = p->next 实际等于 s->next = s,链表断裂;
  • C:s->next = p 会形成环,方向错误;
  • D:p->next = s->next 把 p 的后继指向了未定义位置。
典型考题 · 填空题

填空题(共 6 题)

第七章 · 结构体、共用体及链表

填空题主要考查概念性记忆和关键代码片段:成员的引用形式、结构体定义的分号、共用体长度、字符串赋值函数,以及链表动态内存的申请与释放。

作答提示:空白处的答案要与教材用词一致,如“最长成员”“分号”这类术语不能写成近义表述。

填空题第 1 题结构体 · 成员引用
结构体成员的引用方式有三种:① 结构体变量名.成员名;② ;③ 指针变量名->成员名。

(*指针变量名).成员名

即 (*p).num 这种写法。

三种形式完全等价:

写 法适 用 对 象示 例
变量名.成员名结构体变量a.num
(*指针名).成员名结构体指针(先解引用)(*p).num
指针名->成员名结构体指针(简写形式)p->num

易错:括号不能省!*p.num 会被解释为 *(p.num),编译出错。

填空题第 2 题结构体 · 类型声明
声明结构体类型时,成员表列后的一对花括号外面必须加 (填符号),否则编译出错。

分号 ;

C
  • 结构体类型定义是一条完整的语句,花括号只是语句的一部分,必须以分号结束 —— 这一点和函数定义(不带分号)不同;
  • 考试中这是程序改错题的常客:看到 } 后面没有分号,直接就能标记出错误。
填空题第 3 题共用体 · 内存长度
共用体变量所占内存单元的长度等于其 的长度。

最长成员(占用字节数最多的成员)

共用体的所有成员共用同一段内存,因此容量必须容得下最大的那个成员。

C

对比:结构体的长度约等于各成员之和(并考虑对齐),共用体取最大值。

填空题第 4 题结构体 · 字符数组成员赋值
已知 struct node { int a; char b, c[10]; } x;,要把字符串 "abc" 存入成员 c,应使用语句 。

strcpy(x.c, "abc");

并需在文件开头包含 #include <string.h>。

为什么不能写 x.c = "abc";?

  • 数组名 c 是地址常量,不能作为赋值号的左值;
  • 对字符数组的整体赋值必须使用库函数 strcpy(目标, 源);
  • 若是初始化(在定义时),才可以写 char c[10] = "abc";。
填空题第 5 题链表 · 动态申请空间
建立链表时,为新结点动态申请存储空间应使用 函数。

malloc

C
  • malloc 返回 void *,必须强制类型转换为结点指针类型;
  • 参数是所需字节数,通常写 sizeof(Node);
  • 使用前必须判断是否为 NULL(申请失败时返回 NULL);
  • 需要包含 <stdlib.h>。
填空题第 6 题链表 · 释放空间
删除链表中的结点后,应使用 函数把被删结点的存储空间归还给系统。

free

C
  • free 只能释放由 malloc / calloc / realloc 申请的动态空间;
  • 释放后该指针成为野指针,继续使用属于未定义行为,应立刻置 NULL;
  • 不可重复 free 同一个指针,会导致程序崩溃。
典型考题 · 程序分析结果题

程序分析结果题(共 5 题)

第七章 · 结构体、共用体及链表

这是本考点分值最高、最常见的题型:给出一段完整程序,要求写出运行结果。

解题三步法:① 找出数据结构(结构体 / 共用体 / 链表);② 在草稿纸上画内存图与指针箭头;③ 按语句顺序逐步演算,特别注意格式串与输出顺序。

本组覆盖:结构体整体初始化、共用体成员覆盖、结构体指针修改成员、链表遍历输出、成员表达式中的自增运算。
程序分析结果题第 1 题结构体 · 整体初始化
写出下列程序的运行结果。
C

101,Li

  • 结构体变量 a 在定义的同时整体赋初值 {101, "Li"},按成员顺序依次对应 num 与 name;
  • 输出时必须按成员分别输出:a.num、a.name;
  • 若写成 printf("%d,%s\n", a); 是错误的 —— 结构体变量不能整体输入输出。
程序分析结果题第 2 题共用体 · 成员覆盖
写出下列程序的运行结果(int 占 4 字节,采用小端存放)。
C

A

  • 共用体 u 的成员 i 与 ch 共用同一段内存,起始地址相同;
  • u.i = 65; 存入后,内存中低地址字节为 0x41,即字符 'A'(ASCII 码 65);
  • u.ch 读的正是这个字节,所以输出 A;
  • 这正是共用体“成员互相覆盖”的典型体现。
程序分析结果题第 3 题结构体指针 · 修改成员
写出下列程序的运行结果。
C

10,A
15

  • p 指向 x,所以 p->a、(*p).a、x.a 访问的是同一个存储单元;
  • 第一行输出 10,A;
  • p->a += 5 等价于 x.a = x.a + 5,10 → 15;
  • 第二行输出 15。注意 %c 输出字符,%d 输出整数。
程序分析结果题第 4 题链表 · 遍历输出
写出下列程序的运行结果。
C

123

链表结构为 a → b → c → NULL,三个结点通过 next 依次相连。

  • 第 1 轮:head 指向 a,输出 1,head 后移到 b;
  • 第 2 轮:head 指向 b,输出 2,head 后移到 c;
  • 第 3 轮:head 指向 c,输出 3,head 后移到 NULL;
  • 循环条件不再成立,程序结束。总输出为 123。

本题没有 malloc 也没有 free,结点全部是普通结构体变量,用取地址的方式构成链 —— 这是分析结果题中最常见的“简化链表”。

程序分析结果题第 5 题结构体 · 成员表达式运算
写出下列程序的运行结果。
C

4,13

  • x.a++ 是后缀自增:先取 x.a 的原值 3 参与运算,然后 x.a 自增为 4;
  • 所以 x.b = 3 + 10 = 13;
  • 输出顺序是 x.a 在前、x.b 在后,故结果为 4,13;
  • 注意成员运算符 . 的优先级最高,x.a++ 一定被解释为 (x.a)++。
典型考题 · 程序改错题

程序改错题(共 4 题)

第七章 · 结构体、共用体及链表

改错题的答案必须写成“指出错误 + 说明原因 + 给出正确写法”三要素,只写“改成分号”往往拿不到全分。

本组覆盖结构体与链表最经典的四个坑:缺分号、字符数组整体赋值、指针与变量类型不匹配、插入与删除的指针顺序。

答题模板:第 ① 处错误:______(原因:______),应改为 ______。
程序改错题第 1 题结构体 · 定义与字符数组赋值
下列程序有两处错误,请指出并改正。
C

错误 ①:结构体类型定义结束时缺少分号。应改为 };

错误 ②:字符数组不能用赋值号整体赋值。应改为 strcpy(a1.name, "Li");,并在文件开头加上 #include <string.h>

C改正后的完整程序
  • ① 缺分号:struct 定义是一条完整语句,} 后必须有分号;
  • ② 数组整体赋值:name 是数组名,代表地址常量,不能作赋值号的左值;
  • 改正后输出结果:100,Li;
  • 若题目允许,也可改用 scanf("%d%s", &a1.num, a1.name); 输入。
程序改错题第 2 题结构体 · 指针与变量类型不匹配
下列程序有一处错误,请指出并改正。
C

错误:p = a; —— p 是指针变量,而 a 是结构体变量,两者类型不匹配,不能把结构体变量直接赋给指针。

应改为:

C
  • 结构体指针只能接收结构体变量的地址(&a)或另一个结构体指针的值;
  • 改正后 p 指向 a,程序输出 1;
  • 反面提示:也不要写成 p = a.next; 之后随意解引用,必须先保证赋的是合法有效地址。
程序改错题第 3 题链表 · 插入的指针顺序
下面代码要把结点 s 插入到结点 p 之后,有一处错误,请指出并改正。
C

错误:第二句写成了 p->next = s->next;。此时 s->next 的值就是 p 原来的后继,执行后 p->next 又变回原后继,新结点 s 根本没有被插入链表。

应改为:

C
  • 插入的两步有严格的先后顺序:颠倒会断链,写错目标会丢结点;
  • 记忆口诀:“先接后继,再断前驱”;
  • 等价描述:新结点的指针域先指向原后继,再把前驱结点的指针域指向新结点;
  • 画图验证法:画三个方框 p、s、q(q 为 p 的原后继),按语句逐步画出箭头,一眼就能看出错误所在。
程序改错题第 4 题链表 · 删除后未释放空间
下面代码用于从链表中删除 p 的后继结点,有一处遗漏,请指出并补充。
C

遗漏:free(q);

q 所指结点的空间是用 malloc 动态申请的,摘链之后必须用 free(q) 归还,否则会造成内存泄漏。

C完整的删除代码
  • 顺序必须是“先摘链,再释放”:若先 free 再取 q->next,就访问了已释放的内存,结果错误;
  • 特例补充:若被删的是第一个结点,还要修改头指针:head = head->next;;
  • 若被删的是最后一个结点,则其前驱的 next 应置为 NULL;
  • 考试提醒:改错题若题干要求“删除结点”,摘链 + free 两件事都要做到。
典型考题 · 程序设计题

程序设计题(共 4 题)

第七章 · 结构体、共用体及链表

设计题要求写出完整可运行的代码,分值高、步骤多。建议按“定义类型 → 写函数 → 写主函数 → 输出结果”的顺序书写,并保留必要注释。

本组难度递进:① 结构体数组统计 → ② 结构体作函数参数与返回值 → ③ 建立并遍历链表 → ④ 链表就地逆置。

评分点提示:类型定义正确、输入格式正确、循环边界正确、指针操作正确。即使程序不能完整运行,写出正确的类型定义与核心算法也能得分。
程序设计题第 1 题结构体数组 · 统计
定义学生结构体 struct stu { int num; char name[10]; float score[3]; float sum; float avg; };,输入 5 名学生的学号、姓名和 3 门课成绩,计算并输出每人的总分和平均分。
C参考程序
  • 用结构体数组 s[N] 表示 N 名学生,每个元素是一条完整记录;
  • 输入时注意:score[j] 与 num 前面要加 &,而 name 是字符数组名(本身即地址),不加 &;
  • sum 必须先清零再累加 —— s[i].sum = 0; 漏写会得到随机值;
  • 平均值用总分除以 3;输出 %.1f 保留一位小数;
  • 易错:scanf("%d%s", &s[i].num, s[i].name) 中 s[i].name 不能写成 &s[i].name。
程序设计题第 2 题结构体 · 函数参数与返回值
用结构体表示复数(实部 real、虚部 imag),编写函数求两个复数的和与积,并输出结果。
C参考程序
C运行结果
  • 结构体变量可以作为函数的参数,也可以作为函数的返回值(整体传递,值传递方式);
  • 函数返回值类型要写成结构体类型:struct complex add(...);
  • 复数乘法公式:(a+bi)(c+di) = (ac − bd) + (ad + bc)i,注意实部是减号,最容易写错;
  • 值传递的特点:函数内修改形参不会影响实参;若需要修改实参,应当传递结构体指针。
程序设计题第 3 题链表 · 建立与输出
建立一个单向链表,结点数据域为整型。用尾插法输入 n 个整数(n 由键盘输入),然后从头到尾输出链表中的所有数据。
C参考程序
C运行示例
  • 尾插法要维护 tail 指针,保证每次都 O(1) 接到表尾,避免每插一个结点都从头找尾(O(n²));
  • 新结点的 next 必须先置 NULL,否则链表末尾没有终止标志,遍历会越界;
  • malloc 的返回值要强制类型转换,并检查是否为 NULL;
  • 遍历要用临时指针 p,绝不能直接移动 head,否则头指针丢失,链表无法再被访问;
  • 加分项:程序结束前应遍历链表逐个 free 释放结点空间。
程序设计题第 4 题链表 · 逆置(反序)
在上一题的基础上,编写函数把单向链表就地逆置,并在主函数中输出逆置前、后的链表。
C参考程序
C运行示例
  • 就地逆置三指针法口诀 —— “记后继、掉箭头、双前移”:
    ① r = p->next; 记住后继;② p->next = q; 方向反转;③ q = p; p = r; 两根指针一起前移;
  • 循环结束时 p == NULL,q 指向原表尾,它就是新的头指针;
  • 最关键的一步:head = Reverse(head); 必须用返回值更新 head。若只写 Reverse(head);,head 仍指向原表头(此时已是表尾),链表只剩一个结点可用;
  • 另一种思路是头插法重建:依次取出原表结点头插到新表中,结果同样是反序的,但需要额外的头结点,不属于就地逆置。

本章复习要点回顾

结构体 = 声明类型 → 定义变量 → 引用成员 → 初始化;
链表 = 创建 → 插入 → 删除 → 反序 → 输出,抓住“指针顺序”这一个核心。

三句必背口诀

  • 成员引用:变量用 .,指针用 ->
  • 插入删除:先接后继 / 先摘链
  • 逆置三步:记后继、掉箭头、双前移

四类改错点

  • struct 定义漏分号
  • 结构体整体输入输出
  • 字符数组整体赋值(应用 strcpy)
  • 指针符号用错(-> 与 .)

五个分值主战场

  • 链表的创建(尾插法)
  • 链表的插入与删除
  • 链表的就地逆置
  • 结构体数组 + 统计

按 M 打开目录 · 按 A 展开答案 · 按 Esc 关闭面板

← → 翻页 A 展开答案 M 目录 F 全屏 Esc 关闭
目录