一、说明

这篇笔记基于《21天学通 C++(第8版)》第八章“指针和引用”的核心内容整理而成,但题目、解析和拓展说明均为重新组织后的训练材料。

使用方式很简单:

  • 先独立做题
  • 每题后面立即对照答案
  • 顺手看解析
  • 再把“知识点拓展”当成复盘

整套题都做完,基本就能把这一章最关键的坑和概念吃透。


二、指针基础题(1-20)

题 1

题目: 指针变量中直接保存的内容是什么?

A. 变量的类型信息
B. 变量的值
C. 某个对象所在的内存地址
D. 编译器自动生成的标记

正确答案: C

解析: 指针本质上是一个保存地址的变量,它不是直接保存目标对象的值,而是保存“这个对象在哪里”。

知识点拓展: 学指针时一定先分清 ptr*ptr。前者通常是地址,后者才是地址处的数据。

题 2

题目: 下面哪一项最能描述 &age 的含义?

A. age 的值加 1
B. age 的地址
C. age 的引用类型
D. age 的长度

正确答案: B

解析: & 在这里是取地址运算符,表示变量 age 存放在内存中的位置。

知识点拓展: 同一个符号 & 在不同上下文里角色不同。声明引用时它表示“引用”,表达式里它表示“取地址”。

题 3

题目:int age = 30; int* ptr = &age;,则 *ptr 的值是:

A. age 的地址
B. 30
C. int*
D. 不确定

正确答案: B

解析: ptr 保存的是 age 的地址,*ptr 通过解引用访问这个地址中的整数值,因此结果是 30

知识点拓展: 解引用的前提是指针有效。无效指针一旦解引用,程序可能崩溃。

题 4

题目: 下面哪种写法最适合作为现代 C++ 中空指针的初始化方式?

A. int* ptr = 0;
B. int* ptr = NULL;
C. int* ptr = nullptr;
D. int* ptr;

正确答案: C

解析: 在现代 C++ 中,nullptr 是专门为空指针引入的关键字,语义和类型都更清晰。

知识点拓展: 书里常见 NULL,但实际现代代码里优先用 nullptr

题 5

题目: 下面哪项说法正确?

A. 指针不是变量
B. 指针不占内存
C. 指针也是变量,只是保存的是地址
D. 指针只能指向整数

正确答案: C

解析: 指针和普通变量一样需要声明、占内存、可赋值,只不过它存储的是地址。

知识点拓展: 正因为指针本身也是变量,所以它也有自己的地址。

题 6

题目: 若要声明一个指向 double 的指针,下面哪项正确?

A. double ptr*;
B. *double ptr;
C. double* ptr;
D. pointer double ptr;

正确答案: C

解析: double* ptr; 表示 ptr 是一个指向 double 的指针。

知识点拓展: 很多人会把 * 看成类型的一部分,实际上更安全的理解是:ptr 是指针。

题 7

题目: 对未初始化指针最大的风险是什么?

A. 会导致编译变慢
B. 会导致指针不能参与比较
C. 垃圾值会被当成地址使用
D. 会自动变成 nullptr

正确答案: C

解析: 未初始化指针包含随机值,而这些随机值一旦被解释为地址并解引用,就可能发生非法访问。

知识点拓展: 指针未初始化,比普通变量未初始化更危险,因为它可能让程序访问根本不该访问的内存。

题 8

题目:int age = 30; int* ptr = &age;,下面哪一项会修改 age 的值?

A. ptr = nullptr;
B. *ptr = 40;
C. &ptr = &age;
D. ptr = &ptr;

正确答案: B

解析: *ptr = 40; 表示把 ptr 指向位置上的整数值改为 40,也就是改了 age

知识点拓展: 指针赋值改的是“指向哪里”,解引用赋值改的是“地址里的内容”。

题 9

题目: 指针重新赋值后,改变的是:

A. 原变量的类型
B. 原变量所在内存的大小
C. 指针当前保存的地址
D. 编译器对变量的解释方式

正确答案: C

解析: 指针重新赋值意味着它改为保存另一个地址,从而改为指向另一个对象。

知识点拓展: 这也是指针和引用的重要区别之一:指针可改指向,引用通常不可改绑。

题 10

题目: void* 指针最典型的特点是:

A. 不能保存地址
B. 可以指向任意类型对象的地址
C. 只能指向 void 类型
D. 会自动解引用

正确答案: B

解析: void* 常被称为“无类型指针”,它可以保存任意对象的地址,但使用前通常需要转换。

知识点拓展: 在 C++ 里,void* 没有类型信息,因此不能像具体类型指针那样直接解引用。

题 11

题目: 在表达式环境里,*ptr 的含义是:

A. 取 ptr 的地址
B. 声明 ptr 是指针
C. 访问 ptr 指向位置上的对象
D. 释放 ptr

正确答案: C

解析: * 在表达式里是解引用运算符,用来访问指针指向的对象。

知识点拓展: 同样一个 *,在声明中和在表达式中的含义完全不同,这是初学者高频混淆点。

题 12

题目:int age = 30; int* ptr = &age;,则 ptr&age 的关系是:

A. 一定不同
B. 类型不同但值含义相同
C. ptr 等于 age
D. ptr 等于 *age

正确答案: B

解析: ptr 中保存的是 age 的地址,而 &age 表达式求出的也是 age 的地址,所以地址值一致。

知识点拓展: ptr 是变量,&age 是表达式。两者语义位置不同,但地址值可以相同。

题 13

题目: 如果一个指针已经指向合法对象,以下哪个操作通常是安全的?

A. 对空指针解引用
B. 对野指针解引用
C. 通过解引用读取目标值
D. 对未初始化指针做 delete

正确答案: C

解析: 指针已经指向合法对象时,读写该对象才有意义;其余几项都具有明显风险。

知识点拓展: “安全”这件事和语法往往不是一回事,很多危险代码也能通过编译。

题 14

题目: 下面哪一项体现了“指针保存地址而不是值”?

A. int x = 10;
B. int* p = &x;
C. int& r = x;
D. const int y = 10;

正确答案: B

解析: p = &x 是把 x 的地址交给指针,而不是把 x 的数值 10 直接拷贝进 p

知识点拓展: 如果把数值 10 强行塞给指针,语义上就是把“10 当成地址”,这通常完全错误。

题 15

题目: 指针变量与普通变量相比,最大的特殊性在于:

A. 不能初始化
B. 不能被赋值
C. 它保存的值会被解释为地址
D. 它必须是全局变量

正确答案: C

解析: 指针之所以特殊,不是因为它的语法形式,而是因为其内容会被解释为地址。

知识点拓展: 地址是数值,但不是“普通业务值”;一旦程序把某个数当作地址使用,就进入了内存访问语义。

题 16

题目: 在 64 位环境下,sizeof(int*) 通常更可能是:

A. 1
B. 2
C. 4
D. 8

正确答案: D

解析: 64 位环境的地址宽度通常是 8 字节,因此指针大小通常为 8。

知识点拓展: 指针大小跟平台相关,不要把某个平台上的结果写死进认知里。

题 17

题目: sizeof(char*)sizeof(double*) 在同一平台上通常:

A. 前者更大
B. 后者更大
C. 一样大
D. 一个固定、一个不固定

正确答案: C

解析: 无论指针指向哪种类型,指针变量自己存的都是地址,所以大小通常一样。

知识点拓展: 真正有差异的是 sizeof(char)sizeof(double),而不是它们的指针大小。

题 18

题目: 下面哪项最准确?

A. sizeof(ptr) 取决于 ptr 指向对象的值
B. sizeof(ptr) 取决于平台和地址宽度
C. sizeof(ptr) 恒等于 sizeof(*ptr)
D. sizeof(ptr) 只有运行时才能确定

正确答案: B

解析: 指针本身大小取决于平台地址宽度,而不取决于它当前指向对象的内容。

知识点拓展: sizeof 对于普通对象和指针对象往往反映的是两套完全不同的含义。

题 19

题目:int age = 30; int* ptr = &age;,则下面哪个表达式结果最可能是地址?

A. age
B. *ptr
C. ptr
D. *&age

正确答案: C

解析: ptr 本身通常表示地址,age*ptr 则是整数值。

知识点拓展: *&age 会先取地址再解引用回来,最终仍然是 age 本身,不是地址。

题 20

题目: 指针在 C++ 中之所以强大,是因为它让程序能够:

A. 只在源代码级操作变量
B. 完全忽略内存模型
C. 直接控制和访问内存位置
D. 自动进行垃圾回收

正确答案: C

解析: 指针把程序从“只看变量名”推进到“直接处理地址和内存位置”的层面。

知识点拓展: 强大和危险往往是同一枚硬币的两面。越靠近底层,越需要纪律。


三、动态内存与资源管理题(21-40)

题 21

题目: new int 的直接返回值是什么?

A. 一个整数
B. 一个 int 对象
C. 指向新分配整数空间的指针
D. 一个数组长度

正确答案: C

解析: new int 会在自由存储区申请一块能放 int 的空间,并返回其地址。

知识点拓展: new 返回地址,所以通常要用指针接收。

题 22

题目: 下面哪组配对是正确的?

A. new 对应 delete[]
B. new[] 对应 delete
C. new 对应 delete
D. malloc 对应 delete

正确答案: C

解析: 单对象分配 new 必须配对单对象释放 delete

知识点拓展: 数组分配和释放必须是 new[] / delete[] 配套,不可混用。

题 23

题目: int* nums = new int[10]; 用完后正确的释放方式是:

A. delete nums;
B. delete[] nums;
C. free(nums);
D. 不需要释放

正确答案: B

解析: 动态数组必须用 delete[] 释放,否则行为未定义。

知识点拓展: 这类错误往往不会立刻暴露,但它属于内存管理错误,后果可能很隐蔽。

题 24

题目: 动态内存分配的核心价值是:

A. 让所有对象都变成全局对象
B. 让数组长度在运行时可决定
C. 让代码不需要编译
D. 自动避免内存泄漏

正确答案: B

解析: 动态内存让资源大小可以根据运行时需求决定,而不是在编译期写死。

知识点拓展: 这也是 vector 等容器背后的思想来源之一。

题 25

题目: 哪种情况最符合“内存泄漏”的定义?

A. 使用了静态数组
B. 同一块内存被释放一次
C. 分配的内存失去引用却未释放
D. 指针指向栈变量

正确答案: C

解析: 一旦分配出的内存无法再被程序找到并释放,就形成了泄漏。

知识点拓展: 泄漏的本质不是“占了内存”,而是“资源所有权丢失且不可回收”。

题 26

题目: 下面哪段代码最可能造成内存泄漏?

A. int x = 10;
B. int* p = new int; delete p;
C. int* p = new int[5]; p = new int[10];
D. int* p = nullptr;

正确答案: C

解析: 第二次赋值前没有释放第一块内存,旧地址丢失,因此泄漏。

知识点拓展: 覆盖指针变量是典型泄漏源,尤其在复杂控制流里很容易发生。

题 27

题目: 下面哪一项是对 delete 的正确理解?

A. 它可以释放任意地址
B. 它只能释放由匹配的 new 分配出的资源
C. 它会自动把所有同地址指针都置空
D. 它会把内存永久删除

正确答案: B

解析: delete 只能用于释放由 new 获得的对应资源,不能拿来释放普通变量地址。

知识点拓展: “看起来像地址”不等于“可以 delete”,关键在于这块资源的来源。

题 28

题目: 为什么 delete &age; 是错误的?

A. 因为 age 必须是 double
B. 因为 &age 不是指针
C. 因为 age 不是通过 new 创建的
D. 因为 delete 只能在类里用

正确答案: C

解析: age 是普通自动对象,不是自由存储区分配来的资源,不能交给 delete

知识点拓展: 栈对象和动态对象的生命周期管理方式完全不同。

题 29

题目: 下面哪种做法最可能导致悬空指针?

A. int* p = nullptr;
B. int* p = new int(5); delete p;
C. int x = 3; int* p = &x;
D. const int* p = nullptr;

正确答案: B

解析: 删除之后,指针变量 p 仍然存在,但它原先指向的资源已失效,这就是悬空。

知识点拓展: 很多人误以为 delete 会让指针自动变空,实际上不会。

题 30

题目: delete p; p = nullptr; 这两步连着写的主要意义是:

A. 提高编译速度
B. 防止后续误用悬空指针
C. 让内存变大
D. 让 delete 生效两次

正确答案: B

解析: 释放后手动置空,能减少后续误解引用旧地址的风险。

知识点拓展: 这不是万能安全措施,但在裸指针时代是一种常见自保手段。

题 31

题目: 对同一块动态内存重复调用 delete 会怎样?

A. 一定安全
B. 一定什么都不发生
C. 属于未定义行为
D. 会自动转成 delete[]

正确答案: C

解析: 同一资源只能释放一次,重复释放属于严重内存错误。

知识点拓展: 重复释放和使用已释放资源,都是典型“生命周期违规”。

题 32

题目: new 失败时,默认行为更接近于:

A. 返回随机地址
B. 返回 0 但不提示
C. 抛出异常
D. 自动重试直到成功

正确答案: C

解析: 默认 new 分配失败时会抛出 std::bad_alloc

知识点拓展: 这是现代 C++ 资源分配错误处理的重要入口,和异常机制结合很紧密。

题 33

题目: 如果不想依赖异常处理,而想在分配失败时得到空指针,应使用:

A. new[]
B. new(nothrow)
C. delete(nothrow)
D. nullptr new

正确答案: B

解析: new(nothrow) 在分配失败时返回空指针,而不是抛异常。

知识点拓展: 两种思路都可用,关键是团队要在资源分配失败的策略上保持一致。

题 34

题目: 使用 new(nothrow) 后,最合理的下一步是:

A. 直接解引用
B. 直接 delete
C. 检查返回指针是否为空
D. 立即递增指针

正确答案: C

解析: 既然使用了 nothrow,就意味着分配失败时可能返回空,因此必须检查。

知识点拓展: “检查失败路径”是系统编程里必须形成的条件反射。

题 35

题目: 下列关于动态内存的说法哪项正确?

A. 用完后系统总会立即自动替你释放
B. 手动分配的资源应由程序显式管理
C. new 只适用于类对象
D. 静态数组和动态数组没有区别

正确答案: B

解析: 原始 new/delete 模式下,资源释放责任在程序员。

知识点拓展: 现代 C++ 倾向于通过 RAII 把这种责任交给对象本身而不是手工记忆。

题 36

题目: 为什么说原始 new/delete 容易出错?

A. 因为它们语法太短
B. 因为资源生命周期由程序员手工维护
C. 因为它们只能用于整数
D. 因为它们不能和函数一起使用

正确答案: B

解析: 一旦生命周期靠人手工追踪,就容易出现忘记释放、重复释放、释放过早等问题。

知识点拓展: 智能指针和容器类的价值,本质上就在于减少这种人工管理负担。

题 37

题目: 下列哪一项最能体现“资源所有权”概念?

A. 一个资源应该知道由谁负责释放
B. 指针一定比引用快
C. 所有地址都能复制
D. 所有变量都能 delete

正确答案: A

解析: 内存管理的关键不只是“能访问”,而是“谁负责最终释放”。

知识点拓展: unique_ptr 就是把“唯一所有者”写进类型系统。

题 38

题目: 下面哪个现代 C++ 工具通常可以替代手写动态数组?

A. std::vector
B. std::cin
C. std::endl
D. typedef

正确答案: A

解析: std::vector 能自动管理动态数组容量与释放,是更现代、更安全的做法。

知识点拓展: 真正的工程经验不是“会写裸指针”,而是“知道什么时候不该手写裸指针”。

题 39

题目: 动态分配和释放一块内存后,下面哪项表述更合理?

A. 指针自动失效且自动置空
B. 指针变量还在,但不应再被解引用
C. 可以继续用旧指针安全写入
D. 可以立刻再次 delete

正确答案: B

解析: 资源释放后,指针变量依旧存在,但指向的对象生命周期已经结束。

知识点拓展: 这正是悬空指针的来源:变量还在,目标已死。

题 40

题目: 如果程序运行时间越长,占用内存越多,而功能并未显著增加,首先该怀疑:

A. 指针大小变化
B. 内存泄漏
C. 编译器失效
D. 引用语法错误

正确答案: B

解析: 这是内存泄漏最典型的外部表现之一。

知识点拓展: 长时间运行服务里,泄漏问题远比小脚本更致命。


四、指针运算与数组题(41-60)

题 41

题目:int* p 指向某个整数,则执行 ++p 后,p 通常会:

A. 前进 1 位二进制
B. 前进 1 个字节
C. 前进一个 int 的跨度
D. 变成空指针

正确答案: C

解析: 指针递增按“元素类型大小”移动,不是固定按 1 字节。

知识点拓展: 指针运算的单位不是字节,而是“当前所指类型的元素个数”。

题 42

题目:char* p 执行 ++p,与 int* p 执行 ++p 的主要区别在于:

A. 前者不能递增
B. 后者不能递增
C. 前进的字节数通常不同
D. 语法完全不同

正确答案: C

解析: char 通常占 1 字节,而 int 往往占 4 字节或其他平台相关大小,因此前进步长不同。

知识点拓展: 这是为什么 char* 常被用来做字节级处理。

题 43

题目: 对于数组 int arr[5];,在很多表达式中,arr 会退化为:

A. int
B. int*,指向首元素
C. int&
D. void*

正确答案: B

解析: 数组名在多数表达式里会退化为指向首元素的指针。

知识点拓展: 这就是为什么 arr 常常和 &arr[0] 看起来效果相同。

题 44

题目: arr[2] 与下面哪个表达式等价?

A. *arr + 2
B. *(arr + 2)
C. &arr + 2
D. **arr

正确答案: B

解析: 数组下标本质上就是基址加偏移再解引用。

知识点拓展: 这也是“数组和指针关系紧密”的根本原因。

题 45

题目:int* p = arr;,则 p[3] 与下面哪个表达式等价?

A. *(p + 3)
B. *p + 3
C. &p[3]
D. **(p + 3)

正确答案: A

解析: 指针同样支持下标语法,p[i] 就是 *(p + i)

知识点拓展: 因为下标语法本质上就是一种指针偏移访问。

题 46

题目: 为什么 myNumbers 可以赋给 int* p,但通常不能再写 myNumbers = p

A. 因为数组名不是左值,不能随意改绑定
B. 因为数组名没有类型
C. 因为 p 不是地址
D. 因为数组必须是 const

正确答案: A

解析: 数组名虽然常退化成指针使用,但数组本身不是一个可修改的普通指针变量。

知识点拓展: 这说明“数组像指针”不等于“数组就是普通指针”。

题 47

题目: 对动态数组 int* nums = new int[5];,访问第 3 个元素的表达式可以是:

A. nums(2)
B. *(nums + 2)
C. nums + 2
D. &nums[2] = 3

正确答案: B

解析: 第 3 个元素下标是 2,因此可通过偏移两个元素再解引用得到。

知识点拓展: nums[2]*(nums + 2) 等价。

题 48

题目:int arr[5] = {1,2,3,4,5};,则 *arr 的值是:

A. arr 的地址
B. 1
C. 5
D. sizeof(arr)

正确答案: B

解析: arr 退化为首元素地址,*arr 就是首元素的值,即 1

知识点拓展: 这和 *p 访问指针指向的第一个对象是同一逻辑。

题 49

题目: 在数组遍历中,如果你不断执行 p++,用完之后为什么往往要恢复到初始地址再释放?

A. 因为 delete[] 需要最初 new[] 返回的地址
B. 因为编译器会忘记数组类型
C. 因为释放前必须先排序
D. 因为数组长度会改变

正确答案: A

解析: 如果你把指针移动走了,释放时必须交回原始首地址。

知识点拓展: 这也是很多人更喜欢用额外迭代指针或下标,而不是直接移动拥有者指针的原因。

题 50

题目: 指针加偏移量的本质是:

A. 地址按字节强行增加
B. 指向同类型下一个或第 n 个元素
C. 改变目标对象类型
D. 创建一个新的数组

正确答案: B

解析: 指针运算的语义是“按元素跨度移动”,而不是简单算术。

知识点拓展: 这也是为什么指针类型信息很重要,编译器需要靠它决定步长。

题 51

题目: 下面哪项最准确地描述了数组名?

A. 总是普通变量
B. 总是引用
C. 在多数表达式里表现得像指向首元素的指针
D. 一定等于数组长度

正确答案: C

解析: 数组名的经典行为就是“衰减”为指向首元素的地址。

知识点拓展: 但在 sizeof(arr) 这样的上下文里,数组不会简单退化。

题 52

题目:int arr[5];,下面哪项通常成立?

A. arr == &arr[0]
B. arr == arr[0]
C. arr == *arr
D. arr == sizeof(arr)

正确答案: A

解析: 数组名在比较场景下通常退化为首元素地址,因此和 &arr[0] 一致。

知识点拓展: 这只是“值相同”的效果,不代表两种写法语义上完全等价。

题 53

题目: 为什么说数组和指针“相似但不相同”?

A. 因为数组不能存数据
B. 因为数组没有类型
C. 因为数组名常像指针,但数组本身不是可重绑的普通指针变量
D. 因为指针不能访问元素

正确答案: C

解析: 它们在访问语法上很接近,但对象模型不同。

知识点拓展: 这一点不搞清楚,后面学函数参数和类成员时很容易错。

题 54

题目: int* arrs[100]; 的含义是:

A. 一个包含 100 个 int 的数组
B. 一个指向 100 个 int 的指针
C. 一个包含 100 个 int* 元素的数组
D. 一个二维整型数组

正确答案: C

解析: 这是“指针数组”,数组里的每个元素都是 int*

知识点拓展: 它与 int arr[100]; 是完全不同的声明。

题 55

题目: int arr[100]; 最准确的描述是:

A. 一个指针数组
B. 一个包含 100 个整数的数组
C. 一个指向 100 个整数的单指针变量
D. 一个引用对象

正确答案: B

解析: 它是静态整型数组,而不是“100 个指针”。

知识点拓展: int* arr[100]int arr[100] 是经典容易看错的一组。

题 56

题目: 对指针做 --p 的语义通常是:

A. 回到前一个同类型元素
B. 删除指针
C. 让地址按位清零
D. 让对象值减 1

正确答案: A

解析: 递减指针表示向前移动一个同类型元素跨度。

知识点拓展: 指针加减都体现“类型化地址”的思想。

题 57

题目: 如果 int* p 已经偏移到数组中间,p[0] 访问的是:

A. 原数组首元素
B. 当前 p 指向的元素
C. 数组最后一个元素
D. 未定义固定地址

正确答案: B

解析: 下标是相对于当前指针位置而言的,不一定相对于原数组首地址。

知识点拓展: 所以在“滑动窗口”式算法里,移动指针后再用 p[i] 很常见。

题 58

题目: 指针算术最适合使用在哪类连续存储结构上?

A. 动态数组或静态数组
B. 任意树形结构
C. 所有哈希表
D. 所有类对象

正确答案: A

解析: 因为连续存储结构天然支持“按固定跨度走到下一个元素”。

知识点拓展: 这也是数组访问高效的底层原因。

题 59

题目: 为什么对 int* 指针加 1 不是“地址数值 +1”的直觉用法?

A. 因为 C++ 不支持加法
B. 因为编译器要保证它指向下一个 int 的起始位置
C. 因为地址不能运算
D. 因为 int* 其实是引用

正确答案: B

解析: 如果只加 1 字节,指针可能落在一个整数的中间,这没有意义。

知识点拓展: 编译器之所以能做对,是因为指针类型里包含了元素类型信息。

题 60

题目: 关于数组与指针,下列说法更合理的是:

A. 任何时候都完全等价
B. 完全无关
C. 在访问语法上联系紧密,但对象模型不同
D. 只有在 C 语言中才相似

正确答案: C

解析: 这句话最完整,既承认它们很像,也保留了关键差异。

知识点拓展: 理解“相似但不等价”,是学好指针这一章的一个门槛。


五、const 指针与函数参数题(61-80)

题 61

题目: int* const p = &x; 的含义是:

A. p 不能改指向,但能通过它改 x
B. p 能改指向,但不能改 x
C. px 都不能改
D. p 是常量引用

正确答案: A

解析: const 修饰的是指针本身,因此地址固定,但数据可变。

知识点拓展: 判断时可以从右向左读,很多人会更容易理清 const 绑定谁。

题 62

题目: const int* p = &x; 的含义是:

A. 指针固定,数据可变
B. 指针可变,数据经由该指针不可变
C. 指针和数据都可变
D. 指针和数据都不可变

正确答案: B

解析: 这里 const 修饰的是被指向的数据,所以不能通过 p 修改它。

知识点拓展: 这不代表原对象永远不能被别的非 const 路径修改。

题 63

题目: const int* const p = &x; 的含义是:

A. 指针和所指数据都不可通过它修改
B. 只有指针不可改
C. 只有数据可改
D. 等价于引用

正确答案: A

解析: 这是最严格的一种 const 指针形式,地址和数据都锁死。

知识点拓展: 在函数参数里,如果只是只读观察,一般会偏好这种“高 const 程度”的表达。

题 64

题目: 在函数参数中,若一个参数只读且必须有效,通常更推荐:

A. T*
B. T&
C. const T&
D. T&&

正确答案: C

解析: const T& 既避免拷贝,又表达“只读”语义,非常常见。

知识点拓展: 这就是现代 C++ 函数接口设计的核心套路之一。

题 65

题目: 在函数里使用指针参数前,书里特别强调要做的一件事是:

A. 必须先做类型转换
B. 必须先做递增
C. 检查指针有效性
D. 先 delete

正确答案: C

解析: 指针参数可能为空或无效,因此使用前应先判断。

知识点拓展: 引用参数通常不需要这一步,因为引用语义上默认必须绑定到有效对象。

题 66

题目: 如果函数需要通过参数返回结果,下面哪种接口更常见?

A. void f(const int& in, int& out)
B. void f(int in, int out)
C. void f(const int* in, const int* out)
D. void f(int&& in, int&& out)

正确答案: A

解析: 一个只读输入参数配 const int&,一个输出参数配非常量引用,是非常典型的设计。

知识点拓展: 这比双指针接口更简洁,前提是你能保证对象一定存在。

题 67

题目: 若函数只是读取半径计算面积,不应该修改半径,那么参数更合适的是:

A. double* radius
B. double& radius
C. const double& radius
D. double radius*

正确答案: C

解析: const double& 清楚表达“只读输入”。

知识点拓展: 对基础类型来说,传值和 const& 在性能上差异不大,但语义表达仍然重要。

题 68

题目: 下面哪项是“不能通过这个指针修改数据”的声明?

A. int* p
B. int* const p
C. const int* p
D. int& p

正确答案: C

解析: const int* p 限制了经由 p 修改其所指对象。

知识点拓展: const 修饰层次不一样,语义就完全不同。

题 69

题目: 为什么说函数参数要“尽可能提高 const 程度”?

A. 因为 const 更短
B. 因为 const 让函数一定更快
C. 因为它能减少误修改,提升接口自说明性
D. 因为 const 能代替异常

正确答案: C

解析: const 不只是编译器约束,还是接口文档的一部分。

知识点拓展: 在多人协作中,越清楚的约束越能减少二次误改。

题 70

题目: 下面哪个说法是正确的?

A. 可以把 const int* 随意赋给 int*
B. const 限定可以随意丢掉
C. 不能把“指向 const 的指针”直接赋给“指向非 const 的指针”
D. 任何指针都能自动降级为非 const

正确答案: C

解析: 如果允许这种赋值,就能绕过 const 约束去修改本该只读的数据。

知识点拓展: 类型系统在这里是在保护程序,而不是故意为难你。

题 71

题目: 对于“必须非空而且只读”的参数,相比指针版本,引用版本的优势之一是:

A. 可以表达空值
B. 语法更长
C. 不需要空指针检查
D. 必须手动释放

正确答案: C

解析: 引用语义上应当绑定到有效对象,因此接口层面更强。

知识点拓展: 这也是为什么很多现代接口更偏好引用而不是原始指针。

题 72

题目: 下面哪种写法最适合表达“参数必须存在,而且函数会修改它”?

A. const T&
B. T&
C. const T*
D. const T* const

正确答案: B

解析: 非 const 引用表达“对象一定存在,而且允许被修改”。

知识点拓展: 这类参数常见于交换、填充、输出结果等函数。

题 73

题目: 为什么书里在指针参数计算面积的函数中要检查 ptrPi && ptrRadius && ptrArea

A. 为了提高打印速度
B. 为了避免对空指针解引用
C. 为了让指针更大
D. 为了让 cout 生效

正确答案: B

解析: 如果指针为空就直接解引用,会发生非法访问。

知识点拓展: 指针接口相比引用接口的一大额外负担,就是你必须考虑空值路径。

题 74

题目: 在“输入参数只读、输出参数可写”的函数设计中,下面哪组更合理?

A. const T& in, T& out
B. T& in, const T& out
C. T in, const T out
D. const T* in, const T* out

正确答案: A

解析: 输入是只读引用,输出是可写引用,这最符合语义。

知识点拓展: 参数的 const 设计,应该和“谁是输入、谁是输出”一一对应。

题 75

题目: 下列关于指针参数的描述哪项更合理?

A. 只要能传地址就一定优于引用
B. 指针适合表达“可空”或“可能不存在”
C. 指针一定更快
D. 指针一定更安全

正确答案: B

解析: 指针最常见的额外语义价值之一,就是能表达“没有对象”。

知识点拓展: 也正因为它能为空,所以它比引用多了一层使用风险。

题 76

题目: double* const ptrArea 更强调:

A. 输出地址不能改,但地址中的结果可写
B. 结果永远不能写
C. 该参数一定为空
D. 该参数是数组

正确答案: A

解析: 这是固定指针、可写数据的典型表达。

知识点拓展: 很多 C 风格接口会这样写输出参数。

题 77

题目: 如果函数内部不应该修改输入对象,最差的接口设计通常是:

A. const T&
B. T&
C. const T*
D. const T* const

正确答案: B

解析: 非 const 引用会给函数提供修改能力,和“只读输入”语义冲突。

知识点拓展: 接口设计里,能力越少越安全,除非你确实需要那项能力。

题 78

题目: 为什么说 const 是接口设计工具而不仅仅是语法?

A. 因为它改变 CPU 架构
B. 因为它会自动生成文档
C. 因为它明确告诉调用者和维护者“哪些数据不能改”
D. 因为它让所有函数都内联

正确答案: C

解析: const 让类型直接承载了“可否修改”的契约。

知识点拓展: 好接口的特征之一,就是从函数签名就能看出大部分行为边界。

题 79

题目: 如果一个接口中参数允许为空,那么更自然的形式常常是:

A. T&
B. const T&
C. T*const T*
D. T&&

正确答案: C

解析: 引用通常表达“对象一定存在”,而指针可表达“没有对象”。

知识点拓展: 这也是很多旧接口大量使用指针的原因之一。

题 80

题目: 关于 const 指针和 const 引用,下面哪句总结更准确?

A. 它们只是为了写着好看
B. 它们让编译器和人都更清楚数据是否可改
C. 它们会自动管理内存
D. 它们只在模板中有用

正确答案: B

解析: const 同时是编译约束和可读性约束。

知识点拓展: 如果你总能自然地区分输入参数、输出参数、只读参数,这一节就算真正掌握了。


六、引用与综合应用题(81-100)

题 81

题目: 引用最准确的定义是:

A. 一个能存放多个地址的数组
B. 一个对象的别名
C. 一个自动释放的指针
D. 一个不可修改的变量

正确答案: B

解析: 引用不是新对象,而是已有对象的另一个名字。

知识点拓展: 这也是为什么引用看起来更像“语义绑定”,而不是“地址容器”。

题 82

题目: 声明引用时,下面哪项是必须的?

A. 必须使用 new
B. 必须初始化
C. 必须是全局变量
D. 必须是 const

正确答案: B

解析: 引用一旦声明就必须绑定到某个对象,不能像指针那样先空着。

知识点拓展: 这也是引用通常比裸指针更稳的原因之一。

题 83

题目: int& ref = original; 之后,reforiginal 的关系是:

A. 两个独立整数
B. 一个指针一个变量
C. 同一对象的两个名字
D. 一个数组一个元素

正确答案: C

解析: 引用只是别名,不会新建一份独立副本。

知识点拓展: 观察它们地址相同,是理解“别名”最直观的办法。

题 84

题目: 为什么引用适合作为函数参数来避免拷贝?

A. 因为引用不需要类型
B. 因为引用能直接绑定到调用者对象
C. 因为引用能自动释放内存
D. 因为引用一定更小

正确答案: B

解析: 引用让函数直接操作原对象,而不是先复制一个新对象。

知识点拓展: 对大对象来说,这能明显减少构造和拷贝开销。

题 85

题目: void GetSquare(int& number)number 被声明为引用的主要作用是:

A. 让函数改的是调用者对象本身
B. 让函数一定返回 int
C. 让函数不能修改值
D. 让函数变成模板

正确答案: A

解析: 这样函数内部对 number 的修改会直接反映到调用者对象上。

知识点拓展: 这类函数本质上是“就地修改”接口。

题 86

题目: 如果希望函数读取输入但不能修改输入,应优先考虑:

A. T&
B. const T&
C. T*
D. T

正确答案: B

解析: const T& 兼顾避免拷贝和禁止修改,是最典型的只读参数写法。

知识点拓展: 对复杂对象尤其适合;对小基础类型则更多体现语义价值。

题 87

题目: 下面哪项对引用与指针的比较更合理?

A. 引用一定比指针快很多
B. 指针一定比引用安全
C. 引用更适合表达必然存在的对象
D. 两者在所有设计场景都完全等价

正确答案: C

解析: 这是两者最有价值的语义差异之一。

知识点拓展: 好的 C++ 设计往往先问“语义上是不是允许为空”,再决定用引用还是指针。

题 88

题目: 对于 const int& ref = value;,下面哪项正确?

A. 可以通过 refvalue
B. ref 是空引用
C. 不能通过 ref 修改 value
D. ref 是一个指针数组

正确答案: C

解析: const 引用是只读别名,不能通过它修改原对象。

知识点拓展: const 引用在参数设计中出现频率极高,要形成直觉。

题 89

题目: 下面哪种情况更倾向于用引用而不是指针?

A. 参数可能不存在
B. 需要表达空值
C. 参数必须有效且函数语义上直接操作该对象
D. 需要频繁改指向

正确答案: C

解析: 当对象必须存在时,引用表达更自然。

知识点拓展: “必须有效”这四个字,往往就是引用上场的信号。

题 90

题目: 为什么说引用在很多接口里比指针“更简单”?

A. 因为它不用考虑空值分支
B. 因为它没有类型
C. 因为它一定能管理动态内存
D. 因为它能自动做异常处理

正确答案: A

解析: 引用省掉了“为空怎么办”的接口分支。

知识点拓展: 少一种状态,就少一类 bug。

题 91

题目: 如果一个函数既需要输入,又需要通过参数回传结果,下面哪组最合理?

A. const T& in, T& out
B. T in, T out
C. const T* in, const T* out
D. T&& in, const T& out

正确答案: A

解析: 只读输入配 const 引用,可写输出配非常量引用,这是经典组合。

知识点拓展: 如果参数可能缺失,再考虑换成指针输出。

题 92

题目: 下列哪项最能体现“引用不能像指针那样重指向”?

A. 引用可以为 nullptr
B. 引用声明后通常绑定关系固定
C. 引用必须在堆上
D. 引用不能作为参数

正确答案: B

解析: 引用一旦绑定到对象,通常就代表这层别名关系固定下来。

知识点拓展: 所以引用更适合表达稳定语义,而不是游走式的地址操作。

题 93

题目: 对于大对象参数,如果只读取,为什么常用 const T&

A. 为了增大对象体积
B. 为了避免不必要拷贝
C. 为了强制动态分配
D. 为了自动内联

正确答案: B

解析: 大对象按值传递可能带来很高复制成本,const 引用可以避免这一点。

知识点拓展: 这条规则在容器、字符串、大型结构体中尤其重要。

题 94

题目: 下列哪项更接近现代 C++ 的实践态度?

A. 只要学会裸指针,什么都不用学
B. 应彻底回避理解指针
C. 必须理解裸指针,但日常代码尽量减少手写 new/delete
D. 引用已经完全淘汰指针

正确答案: C

解析: 理解底层机制是必须的,但工程上更强调安全抽象。

知识点拓展: 这就是“会原理”和“会写工程代码”的区别。

题 95

题目: 下面哪一项是更符合现代风格的动态数组写法?

A. int* nums = new int[n];
B. std::vector<int> nums(n);
C. int nums[] = n;
D. delete[] n;

正确答案: B

解析: vector 自动管理容量和释放,能显著减少裸内存错误。

知识点拓展: 第八章学的是原理,但真正写业务代码时,容器通常是第一选择。

题 96

题目: 下列哪种场景最适合继续使用原始指针?

A. 所有普通业务对象传参
B. 需要表达“可能为空”或与底层 API 交互
C. 所有字符串操作
D. 所有动态数组管理

正确答案: B

解析: 原始指针在底层接口、可空语义、资源边界交互中仍有价值。

知识点拓展: 关键不是“禁用原始指针”,而是“不要滥用它来承担所有工作”。

题 97

题目: 如果某个参数不应该为空,而你仍然把它设计成裸指针,最大的额外成本是:

A. 代码行数减少
B. 调用方更难用
C. 你和调用方都得额外处理空值可能性
D. 编译器自动报错

正确答案: C

解析: 这会让接口状态空间变大,本来不需要的空值分支也被引入了。

知识点拓展: 很多糟糕接口的问题,不是“功能不够”,而是“允许了不该允许的状态”。

题 98

题目: 对于“谁拥有资源、何时释放资源”这一类问题,第八章真正训练的是:

A. 输出格式技巧
B. 生命周期意识
C. 变量命名规范
D. 预处理器语法

正确答案: B

解析: 指针和动态内存最核心的训练目标,就是建立生命周期和所有权意识。

知识点拓展: 后续的构造函数、析构函数、智能指针、移动语义,其实都围绕这件事展开。

题 99

题目: 学完这一章后,面对一个函数接口,最值得先问的问题不包括:

A. 这个参数会不会为空
B. 这个对象由谁释放
C. 这里是否需要修改对象
D. 这个变量名是不是足够长

正确答案: D

解析: 命名当然重要,但这一章更核心的是空值、所有权、生命周期和可变性。

知识点拓展: 真正的底层 bug 很少源于名字,更多源于资源边界和状态边界。

题 100

题目: 如果你做完整套题后只能记住一句话,下面哪句最值得记住?

A. 指针只是语法花样
B. 引用一定能代替所有指针
C. 地址、对象和值不是同一回事;生命周期管理比语法更重要
D. newvector 更现代

正确答案: C

解析: 这句话把第八章真正的核心压缩出来了。指针和引用只是入口,背后真正训练的是内存模型与生命周期意识。

知识点拓展: 只会写 *& 不算学会指针。能判断“谁拥有资源、何时失效、该用值/引用/指针哪一种语义”才算入门。


七、刷题建议

如果你准备把这一章真正学扎实,可以这样复习:

  1. 先做 1-20,确保 &*、地址和值不再混。
  2. 再做 21-40,重点盯住 new/delete、泄漏、悬空、重复释放。
  3. 再做 41-60,把数组和指针关系彻底理顺。
  4. 再做 61-80,把 const 的修饰层次吃透。
  5. 最后做 81-100,把“接口设计”和“现代 C++ 实践”连起来。

如果你能把整套题做到:

  • 看见一段指针代码时不发怵
  • 能判断是否有生命周期问题
  • 能说出为什么该用 const T&
  • 知道为什么现代代码尽量少写裸 new/delete

那这一章就真的掌握住了。