← 练习册目录

第 4 章 指针、数组与字符串

red wenzi · 2026-09-15 · 编程语言 · C++ · 练习册 · 20 题
📝 本章练习:20 题(A 识别 / B 理解 / C 改错 / D 写程序)。先自己做完再看答案——A、B 档不翻书做,C、D 档必须真的编译运行。

4.1 指针与 const

A1识别判断题:const int* p; 表示不能通过 p 修改指向的值,但可以改变 p 的指向。

答:______________

✅ 查看答案与解析

答案 对

解析:const int* p 是“指向常量的指针”:*p 不能改,p 能改。int* const p 才是“常量指针”:指向不能改。

A2识别判断题:C++ 里写空指针应该用 nullptr,而不是 NULL。

答:______________

✅ 查看答案与解析

答案 对

解析:NULL 在 C++ 里通常是整数 0,重载解析时会挑错函数;nullptr 是类型安全的空指针。

B1理解选择题:执行 int* pa, pb; 之后,pb 是什么类型?

int*

int

编译错误

void*

答:______________

✅ 查看答案与解析

答案 B

解析:星号只跟着第一个名字。pa 是 int*,pb 是 int。想两个都是指针要写 int *pa, *pb;。

C1应用改错题:下面代码用 NULL 调用重载函数,结果调错了版本。
void f(int) { }
void f(int*) { }
f(NULL);   // 想调用指针版本,实际调了 int 版本
✅ 查看答案与解析

答案 f(nullptr); // nullptr 是真正的空指针,会选指针版本

解析:NULL 是整数 0,重载解析选择了 f(int);nullptr 类型安全,一定选中 f(int*)。

D1创造写程序:写一个函数 swapPtr(int* a, int* b),用指针交换两个 int,并在 main 中验证。

提示:注意判空:if (!a || !b) return;

✅ 查看答案与解析

答案 参考答案:

解析:指针可能为空,使用前先判空;通过解引用 *a 修改原变量。

#include <iostream>
void swapPtr(int* a, int* b) {
    if (!a || !b) { return; }
    int t = *a; *a = *b; *b = t;
}
int main() {
    int x = 3, y = 5;
    swapPtr(&x, &y);
    std::cout << x << ' ' << y << '\n';   // 5 3
    return 0;
}

4.2 数组退化与字符串

A1识别判断题:把数组传给函数时,数组名会退化为指向首元素的指针。

答:______________

✅ 查看答案与解析

答案 对

解析:void f(int arr[]) 等价于 void f(int* arr),长度信息在传参时就丢了。

A2识别判断题:原生数组越界访问会像 Java 一样抛异常。

答:______________

✅ 查看答案与解析

答案 错

解析:原生数组不检查越界,a[5] 只是访问了别人的内存,结果不可预测(未定义行为)。

B1理解选择题:int a[5]; 在定义它的作用域里,sizeof(a) / sizeof(a[0]) 得到什么?

指针大小

数组元素个数(5)

数组总字节数

编译错误

答:______________

✅ 查看答案与解析

答案 B

解析:在定义作用域里 sizeof(a) 是数组总字节数(20),除以单元素大小(4)得到个数 5。一旦退化成指针,sizeof 就变 8。

C1应用改错题:函数里想用 sizeof 求传入数组的长度,但结果不对。
void printCount(int arr[]) {
    int n = sizeof(arr) / sizeof(arr[0]);  // 结果不对!
    std::cout << n;
}
✅ 查看答案与解析

答案 函数参数里的数组写法只是装饰,arr 是 int*,sizeof(arr) 是 8。改成:

解析:要拿长度必须额外传参数,或用 std::vector<int>(自带 size())。

void printCount(int arr[], int n) { ... }   // 额外传长度
// 或改用 std::vector / std::array / std::span
D1创造写程序:读入 n 个数存进 std::vector,输出其中的最大值。

提示:遍历时维护最大值变量。

✅ 查看答案与解析

答案 参考答案:

解析:需要动态长度时优先用 std::vector,而不是原生数组。

#include <iostream>
#include <vector>
int main() {
    int n;
    std::cin >> n;
    std::vector<int> v(n);
    for (int i = 0; i < n; ++i) { std::cin >> v[i]; }
    int mx = v[0];
    for (int x : v) { if (x > mx) { mx = x; } }
    std::cout << mx << '\n';
    return 0;
}

4.3 动态内存:new 与 delete

A1识别判断题:new[] 分配的内存必须用 delete[] 释放。

答:______________

✅ 查看答案与解析

答案 对

解析:int* arr = new int[10]; 必须 delete[] arr;。只写 delete 是未定义行为。

A2识别判断题:new 出来的内存可以交给 free 释放。

答:______________

✅ 查看答案与解析

答案 错

解析:两套内存函数绝对不能混用:new 不能 free,malloc 不能 delete。混用是未定义行为,常表现为在毫不相关的地方崩溃。

B1理解选择题:std::malloc 返回什么类型的指针?

int*

char*

void*

nullptr

答:______________

✅ 查看答案与解析

答案 C

解析:malloc 返回 void*,不能直接解引用和做算术,要转回具体类型。

C1应用改错题:下面代码混用了两套内存函数,且数组释放方式错误。
int* a = new int[10];
std::free(a);      // 错误 1:new 的内存不能 free

int* b = new int[10];
delete b;          // 错误 2:new[] 必须配 delete[]
✅ 查看答案与解析

答案 delete[] a;

解析:两处都改成 delete[]。记住配对规则:new↔delete、new[]↔delete[]、malloc↔free。

delete[] b;
D1创造写程序:用 new[] 分配一个 5 个 int 的数组,初始化后求和,最后用正确方式释放。

提示:new int[5]{} 会把元素初始化为 0。

✅ 查看答案与解析

答案 参考答案:

解析:new[] 之后记得 delete[];更省心的做法是直接用 std::vector。

#include <iostream>
int main() {
    int* arr = new int[5]{1, 2, 3, 4, 5};
    int sum = 0;
    for (int i = 0; i < 5; ++i) { sum += arr[i]; }
    std::cout << sum << '\n';   // 15
    delete[] arr;
    return 0;
}

4.4 RAII 与智能指针

A1识别判断题:std::unique_ptr 不能拷贝,只能移动。

答:______________

✅ 查看答案与解析

答案 对

解析:独占所有权:同一时刻对象只属于一个人。不能拷贝(拷贝会复制裸指针导致双重释放),只能 std::move。

A2识别判断题:std::shared_ptr 用引用计数,最后一个引用消失时释放对象。

答:______________

✅ 查看答案与解析

答案 对

解析:shared_ptr 维护引用计数,计数归零自动释放。代价是原子操作开销,只在真正需要共享所有权时用。

B1理解选择题:写链表、树、图这种“拥有”关系,优先用哪个智能指针?

shared_ptr

unique_ptr

weak_ptr

裸指针

答:______________

✅ 查看答案与解析

答案 B

解析:unique_ptr 无额外开销、语义最清楚:同一对象只属于一个人。shared_ptr 只在确实需要共享时才用。

C1应用改错题:下面代码会把同一块内存释放两次。
Node* raw = new Node();
std::shared_ptr<Node> a(raw);
std::shared_ptr<Node> b(raw);   // 两个 shared_ptr 各自管理同一块内存
✅ 查看答案与解析

答案 从同一个 shared_ptr 拷贝:

解析:绝不用裸指针 new 之后交给两个 shared_ptr。要共享就从同一个 shared_ptr 拷贝。

std::shared_ptr<Node> a = std::make_shared<Node>();
std::shared_ptr<Node> b = a;   // 计数变 2,归零时只释放一次
D1创造写程序:用 std::unique_ptr 建两个链表节点(每个节点含 int value 和 std::unique_ptr<Node> next),输出两节点的值,不需要任何 delete。

提示:std::make_unique<Node>();Node 定义里 next 用 unique_ptr。

✅ 查看答案与解析

答案 参考答案:

解析:unique_ptr 析构时自动释放它持有的 next,递归释放整条链,不需要手写 delete。

#include <iostream>
#include <memory>
struct Node {
    int value = 0;
    std::unique_ptr<Node> next;
};
int main() {
    auto head = std::make_unique<Node>();
    head->value = 1;
    head->next = std::make_unique<Node>();
    head->next->value = 2;
    std::cout << head->value << ' ' << head->next->value << '\n';   // 1 2
    return 0;   // 离开作用域自动释放整条链
}
📚 相关概念:编译与链接 · STL · map / set · 迭代器