Chapter 4:Compound Types
对齐第6版 Chapter 4:掌握数组、C 字符串与 string 类、结构体/联合体/枚举、指针算术、自由存储区及 new/delete 的边界与所有权。
学习目标
- 能比较数组、字符串、结构体、联合体和枚举的数据形状,选择能直接表达不变量的类型
- 能绘制指针、数组首地址、长度、动态对象和所有者之间的关系,并验证每次解引用处于合法生命周期与范围
- 能复现数组越界、缺失字符串终止符、错误 union 活动成员和动态内存泄漏,给出容器或 RAII 修复
机制总览
Chapter 4:Compound Types:机制路径
- 1
为什么复合类型首先描述数据形状
Chapter 3 选择单个值的表示,Chapter 4 把值组合成序列、记录、互斥状态和间接关系。数组表示固定数量同类型元素,struct 表示同时存在的异类字段,union 让候选表示共享存储,enum 命名有限状态,pointer 表示某个对象的地址。它们不是语法替代品,选择错误会把长度、活动成员或所有权藏到注释中。
- 2
数组是固定长度的连续同类元素
数组类型包含元素类型和长度。 int scores[4] 有四个 int,合法下标是 0、1、2、3,即半开区间 [0,4) 。初始化列表按顺序提供首值,未提供的剩余元素执行值初始化。
- 3
C 字符串与 string 类有不同长度契约
C 字符串用 char 数组保存字符,并以 \0 终止;容量必须包含终止符。
章级决策实验
Chapter 4:Compound Types:机制与证据
切换《Chapter 4:Compound Types》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。
选择推理阶段
当前阶段 · 为什么复合类型首先描述数据形状
Chapter 3 选择单个值的表示,Chapter 4 把值组合成序列、记录、互斥状态和间接关系。数组表示固定数量同类型元素,struct 表示同时存在的异类字段,union 让候选表示共享存储,enum 命名有限状态,pointer 表示某个对象的地址。它们不是语法替代品,选择错误会把长度、活动成员或所有权藏到注释中。
可核验证据
从干净构建开始,以固定输入运行本节示例,再加入一个边界或故障场景验证「为什么复合类型首先描述数据形状」的状态变化。
学完《Chapter 4:Compound Types》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。
失效—证据矩阵
Chapter 4:Compound Types:失效与核验
为什么复合类型首先描述数据形状
典型失效
若只复述「为什么复合类型首先描述数据形状」结论而不追踪状态、所有权和失败路径,示例扩展成多文件或多对象程序后就容易偏离预期。
核验证据
从干净构建开始,以固定输入运行本节示例,再加入一个边界或故障场景验证「为什么复合类型首先描述数据形状」的状态变化。
数组是固定长度的连续同类元素
典型失效
若只复述「数组是固定长度的连续同类元素」结论而不追踪状态、所有权和失败路径,示例扩展成多文件或多对象程序后就容易偏离预期。
核验证据
从干净构建开始,以固定输入运行本节示例,再加入一个边界或故障场景验证「数组是固定长度的连续同类元素」的状态变化。
C 字符串与 string 类有不同长度契约
典型失效
若只复述「C 字符串与 string 类有不同长度契约」结论而不追踪状态、所有权和失败路径,示例扩展成多文件或多对象程序后就容易偏离预期。
核验证据
从干净构建开始,以固定输入运行本节示例,再加入一个边界或故障场景验证「C 字符串与 string 类有不同长度契约」的状态变化。
为什么复合类型首先描述数据形状
Chapter 3 选择单个值的表示,Chapter 4 把值组合成序列、记录、互斥状态和间接关系。数组表示固定数量同类型元素,struct 表示同时存在的异类字段,union 让候选表示共享存储,enum 命名有限状态,pointer 表示某个对象的地址。它们不是语法替代品,选择错误会把长度、活动成员或所有权藏到注释中。
↡若干值按顺序、命名字段、互斥候选或间接引用等规则组成的结构及其不变量。数组是固定长度的连续同类元素
数组类型包含元素类型和长度。int scores[4] 有四个 int,合法下标是 0、1、2、3,即半开区间 [0,4)。初始化列表按顺序提供首值,未提供的剩余元素执行值初始化。
int scores[4]{92, 85, 77, 100};
int total{0};
for (int score : scores) {
total += score;
}数组对象知道总大小,但在多数表达式和函数参数中会转换为首元素指针,长度不随裸指针传递。尾后指针可作为遍历终点,却不能解引用。二维数组是数组的数组,行列边界要分别证明。
C 字符串与 string 类有不同长度契约
C 字符串用 char 数组保存字符,并以 \0 终止;容量必须包含终止符。strlen 寻找终止符而不是读取数组类型长度,若终止符缺失会越过数组。输入到 char 数组还要限制最大字符数。
char shortName[4]{'A', 'd', 'a', '\0'};
std::string fullName{"Ada Lovelace"};
std::cout << shortName << " / " << fullName.size() << '\n';std::string 自行管理动态容量和逻辑长度,支持赋值、拼接与比较,通常比裸 char 缓冲更安全。它仍有索引边界,size() 返回字符单元数量,不自动解决 Unicode 用户可见字符计数。
struct、union 与 enum 表达不同组合
struct 的所有成员同时存在,适合一条记录;union 的成员共享存储,一次只有一个活动成员;enum 为有限状态命名,避免用魔法整数猜语义。
↡由同时存在的异类命名成员组成的记录类型,每个成员拥有自己的存储与生命周期。enum class Status { pending, ready, failed };
struct Job {
std::string name;
Status status;
int attempts;
};union 若与标签分开维护,任何不同步都会让程序读取错误活动成员。现代代码常用 std::variant 把标签和存储绑在一起;本章理解 union 的核心是“共享存储不等于所有成员同时有值”。
指针保存地址,不携带所有权和长度
指针类型说明所指对象类型,&object 取得地址,*pointer 解引用访问对象。解引用前必须同时证明:指针非空、对象仍存活、地址对齐且访问落在对象/数组合法范围。
int value{42};
int* pointer{&value};
*pointer = 43;指针加一按元素大小前进,不是固定加一个字节。pointer[i] 与 *(pointer + i) 相关,但只有在同一数组对象及尾后范围内形成和比较这些指针才有定义。
自由存储区需要明确 owner
new 在自由存储区创建对象并返回地址,delete 结束单对象,delete[] 匹配数组分配。每次成功分配必须有唯一释放责任;提前 return、异常、复制指针和多出口都会让手工协议变脆弱。
std::size_t count{4};
int* data{new int[count]{}};
// 使用 data[0] 到 data[count - 1]
delete[] data;
data = nullptr;设置 nullptr 只能避免该变量继续悬空,不能更新其他别名。现代实现优先用 std::vector<int> 管动态序列、std::array<int,N> 管固定序列,或用 std::unique_ptr<T[]> 表达单一所有权;但理解 new/delete 能解释这些设施在替你维护什么。
组合类型时让边界一起移动
数组的 struct、指向 struct 的指针、包含数组的 struct 都合法,但每增加一层都要保留对应边界。返回局部数组地址会悬空;返回动态数组地址若不说明 owner 会泄漏;把数组参数写成指针后仍需另传 count。
std::array 保留固定长度并提供容器接口,std::vector 让长度随对象移动,std::string 管理字符序列。这些替代方案的共同价值是把地址、长度和生命周期绑定到同一 owner。
四类故障揭示四种不变量
先预测 sanitizer、输出或内存工具可能看到什么,再运行故障版本。未定义行为的某次结果只是症状,不是规则;真正证据是语言契约被破坏的位置,以及修复后边界如何由类型或 owner 强制保持。
三步选择和验证复合类型
第一步:先画数据形状
判断字段是同时存在、互斥、有限状态、固定序列还是间接关系;写出长度、活动成员与合法状态。
小结
- 数组表达固定连续同类元素,转换为裸指针后不再携带长度,尾后指针不能解引用
- C 字符串依赖
\0,string 类自行管理长度;两者都有各自边界 - 结构体成员同时存在,union 只有一个活动成员,枚举命名有限状态
- 指针只保存地址,不自动携带长度、owner 或生命周期;解引用需要四项联合证明
- new/delete 展示自由存储区协议,容器与 RAII 将地址、长度和释放责任绑定到对象
练习
- 问题 1:选择形状。 一条传感器记录、有限状态和三十个同类样本分别用什么类型?
- 问题 2:证明指针访问。 解引用
data[i]前必须提供哪些证据?
- 问题 3:修复动态数组。 手工 new[]/delete[] 在多出口函数中有何风险,怎样重构?
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 数据形状
- 值按序列、字段、互斥候选或间接关系组成的结构与不变量。
- 数组
- 固定数量同类型连续元素组成的对象。
- C 字符串
- 以空字符终止的 char 序列。
- 结构体
- 由同时存在的异类命名成员组成的记录类型。
- 活动成员
- union 当前已构造并允许读取的成员。
- 指针
- 保存某种对象地址、解引用依赖有效性契约的类型化值。
- 自由存储区
- 由 new 创建并需显式或由 RAII 结束生命周期的动态对象区域。