Skip to content

Commit 11afb67

Browse files
committed
对 C 语言系统教程的第十章和第十一章添加了参考答案。
1 parent bb35dc3 commit 11afb67

2 files changed

Lines changed: 232 additions & 15 deletions

File tree

documents/vol1-fundamentals/c_tutorials/07B-pointers-arrays-const.md

Lines changed: 148 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
---
22
chapter: 1
33
cpp_standard:
4-
- 11
4+
- 11
55
description: 深入理解数组名退化为指针的机制、const 与指针的四种组合、NULL 指针和野指针的防范,为学习 C++ 引用和智能指针打下基础
66
difficulty: beginner
77
order: 10
88
platform: host
99
prerequisites:
10-
- 指针入门:地址的世界
10+
- 指针入门:地址的世界
1111
reading_time_minutes: 11
1212
tags:
13-
- host
14-
- cpp-modern
15-
- beginner
16-
- 入门
13+
- host
14+
- cpp-modern
15+
- beginner
16+
- 入门
1717
title: 指针与数组、const 和空指针
1818
---
19+
1920
# 指针与数组、const 和空指针
2021

2122
上一篇里我们掌握了指针的基本操作——声明、初始化、取地址、解引用、指针运算。现在我们来啃指针里几个比较绕但非常重要的应用:数组和指针之间到底是什么关系,`const` 和指针组合在一起有多少种意思,以及 NULL 指针和野指针为什么这么危险。
@@ -33,7 +34,7 @@ title: 指针与数组、const 和空指针
3334

3435
我们接下来的所有实验都在这个环境下进行:
3536

36-
- 平台:Linux x86\_64(WSL2 也可以)
37+
- 平台:Linux x86_64(WSL2 也可以)
3738
- 编译器:GCC 13+ 或 Clang 17+
3839
- 编译选项:`-Wall -Wextra -std=c17`
3940

@@ -264,6 +265,53 @@ std::unique_ptr<int> p = std::make_unique<int>(42);
264265
const int* linear_search(const int* data, size_t count, int target);
265266
```
266267
268+
### 练习 1 参考答案
269+
270+
```c
271+
#include <stdio.h>
272+
const int* linear_search(const int* data, size_t count, int target);
273+
274+
int main(void) {
275+
const int arr[] = {10, 21, 32, 43, 54, 65, 76, 87, 98, 9};
276+
int target = 43;
277+
278+
// 使用 sizeof 自动计算数组大小
279+
size_t count = sizeof(arr) / sizeof(arr[0]);
280+
281+
const int* result = linear_search(arr, count, target);
282+
283+
if (result != NULL) {
284+
printf("找到了目标 %d,位于地址 %p,是数组的第 %llu 个元素。\n",
285+
target, (void*)result, (unsigned long long)(result - arr));
286+
} else {
287+
printf("未找到目标 %d。\n", target);
288+
}
289+
290+
return 0;
291+
}
292+
293+
const int* linear_search(const int* data, size_t count, int target) {
294+
// 防御性编程:如果指针为空,或者数量为0,直接返回 NULL
295+
if (data == NULL || count == 0) {
296+
return NULL;
297+
}
298+
for (size_t i = 0; i < count; i++) {
299+
if (*(data + i) == target) {
300+
return data + i;
301+
}
302+
}
303+
return NULL;
304+
}
305+
```
306+
307+
终端应当输出:
308+
309+
```text
310+
找到了目标 43,位于地址 00000000005ffe5c,是数组的第 3 个元素。
311+
```
312+
313+
如果出现乱码,可能是字符编码的问题
314+
267315
### 练习 2:指针版数组反转
268316

269317
实现一个原地反转数组的函数,只使用指针算术(两个指针从两端向中间靠拢),不使用数组下标:
@@ -275,6 +323,80 @@ const int* linear_search(const int* data, size_t count, int target);
275323
void reverse_array(int* data, size_t count);
276324
```
277325
326+
### 练习 2 参考答案
327+
328+
```c
329+
void reverse_array(int* data, size_t count);
330+
331+
int main(void) {
332+
int arr[] = {10, 21, 32, 43, 54, 65, 76, 87, 98, 9};
333+
int arr_size = sizeof(arr) / sizeof(arr[0]);
334+
335+
printf("before reverse_array:\n");
336+
for (int i = 0; i < arr_size; i++) {
337+
printf("%d\n", arr[i]);
338+
}
339+
340+
reverse_array(arr, arr_size);
341+
342+
printf("after reverse_array:\n");
343+
for (int i = 0; i < arr_size; i++) {
344+
printf("%d\n", arr[i]);
345+
}
346+
347+
return 0;
348+
}
349+
350+
void reverse_array(int* data, size_t count) {
351+
if (data == NULL) {
352+
printf("data is null\n");
353+
return;
354+
}
355+
if (count == 0) {
356+
printf("data is empty\n");
357+
return;
358+
}
359+
360+
int* start = data;
361+
int* end = data + count - 1;
362+
int temp = 0;
363+
while (start < end) {
364+
temp = *start;
365+
*start = *end;
366+
*end = temp;
367+
start++;
368+
end--;
369+
}
370+
}
371+
```
372+
373+
终端应当输出:
374+
375+
```text
376+
before reverse_array:
377+
10
378+
21
379+
32
380+
43
381+
54
382+
65
383+
76
384+
87
385+
98
386+
9
387+
after reverse_array:
388+
9
389+
98
390+
87
391+
76
392+
65
393+
54
394+
43
395+
32
396+
21
397+
10
398+
```
399+
278400
### 练习 3:const 练习
279401

280402
判断以下每个声明中,哪些操作是合法的,哪些会编译错误:
@@ -291,6 +413,25 @@ const int* const p3 = &value;
291413
// px = &other; // 修改指针指向
292414
```
293415

416+
### 练习 3 参考答案
417+
418+
```c
419+
int value = 42, other = 100;
420+
421+
const int* p1 = &value;
422+
int* const p2 = &value;
423+
const int* const p3 = &value;
424+
425+
*p1 = other; //不合法,因为p1是指向 const int 类型的指针,无法修改其值
426+
*p2 = other; //合法,因为p2是指向 int 类型的 const 指针,不能指向其它地址,但可以修改其值
427+
*p3 = other; //不合法,因为p3既是指向 const int 类型的指针,也是 const 指针,所以既不能指向其它地址,也不能修改其值
428+
429+
p1 = &other; //合法,因为p1不是 const 类型的指针,所以可以指向其它地址
430+
p2 = &other; //不合法,因为p2是指向 int 类型的 const 指针,不能指向其它地址
431+
p3 = &other; //不合法,因为p3既是指向 const int 类型的指针,也是 const 指针,所以既不能指向其它地址,也不能修改其值
432+
433+
```
434+
294435
## 参考资源
295436

296437
- [cppreference: 指针声明](https://en.cppreference.com/w/c/language/pointer)

documents/vol1-fundamentals/c_tutorials/08A-multi-level-pointers.md

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
---
22
chapter: 1
33
cpp_standard:
4-
- 11
4+
- 11
55
description: 深入理解多级指针的内存模型和实际使用场景,区分指针数组与数组指针,掌握 cdecl 声明读法和多级 const 指针的组合
66
difficulty: beginner
77
order: 11
88
platform: host
99
prerequisites:
10-
- 指针与数组、const 和空指针
10+
- 指针与数组、const 和空指针
1111
reading_time_minutes: 9
1212
tags:
13-
- host
14-
- cpp-modern
15-
- beginner
16-
- 入门
17-
- 基础
13+
- host
14+
- cpp-modern
15+
- beginner
16+
- 入门
17+
- 基础
1818
title: 多级指针与声明读法
1919
---
20+
2021
# 多级指针与声明读法
2122

2223
上一篇我们把指针和数组、`const`、NULL 的关系理清楚了。现在来啃指针里更绕的部分——多级指针(指向指针的指针)、指针数组和数组指针那对"混淆双胞胎",以及看到 `const int* const *` 这种声明时不至于大脑宕机的方法。
@@ -36,7 +37,7 @@ title: 多级指针与声明读法
3637

3738
我们接下来的所有实验都在这个环境下进行:
3839

39-
- 平台:Linux x86\_64(WSL2 也可以)
40+
- 平台:Linux x86_64(WSL2 也可以)
4041
- 编译器:GCC 13+ 或 Clang 17+
4142
- 编译选项:`-Wall -Wextra -std=c17`
4243

@@ -302,6 +303,81 @@ void free_matrix(int** matrix, int rows);
302303
void fill_matrix(int** matrix, int rows, int cols, int value);
303304
```
304305
306+
### 参考答案
307+
308+
```c
309+
#include <stdio.h>
310+
#include <stdlib.h>
311+
312+
int** allocate_matrix(int rows, int cols) {
313+
if (rows < 1 || cols < 1) {
314+
return NULL;
315+
}
316+
317+
int** matrix= malloc(sizeof(int*) * rows);
318+
if (matrix == NULL) {
319+
return NULL; //防止内存分配失败,但这不太可能吧...以防万一,还是写上吧,如果程序在1978年的硬件上跑呢(笑)
320+
}
321+
322+
for (int i = 0; i < rows; i++) {
323+
matrix[i] = malloc(sizeof(int) * cols);
324+
if (matrix[i] == NULL) { //内存不够,取消分配,并释放内存。
325+
for (int j = 0; j < i; j++) {
326+
free(matrix[j]);
327+
}
328+
free(matrix);
329+
return NULL;
330+
}
331+
}
332+
return matrix;
333+
}
334+
335+
void free_matrix(int** matrix, int rows) {
336+
if (matrix == NULL) {
337+
return;
338+
}
339+
340+
for (int i = 0; i < rows; i++) {
341+
free(matrix[i]);
342+
}
343+
free(matrix);
344+
}
345+
346+
void fill_matrix(int** matrix, int rows, int cols, int value) {
347+
if (rows < 1 || cols < 1 || matrix == NULL) {
348+
return;
349+
}
350+
351+
for (int i = 0; i < rows; i++) {
352+
for (int j = 0; j < cols; j++) {
353+
matrix[i][j] = value;
354+
}
355+
}
356+
}
357+
358+
```
359+
360+
```text
361+
兴致大发,来写一个图示awa
362+
[M](这个是matrix)(int** 类型)
363+
[M_rows_i](这个是matrix[rows])(int* 类型)
364+
[Value](这个是matrix的值)(int类型)
365+
366+
[M]--->[M_rows_0][M_rows_1][M_rows_2]...[M_rows_n]
367+
| | | | |
368+
↓ |—————————|—————————|—————————————|————>第2次解引用
369+
第1次解引用 ↓ ↓ ↓ ↓
370+
[Value] [Value] [Value] [Value]
371+
[Value] [Value] [Value] [Value]
372+
[Value] [Value] [Value] [Value]
373+
[Value] [Value] [Value] [Value]
374+
[Value] [Value] [Value] [Value]
375+
......
376+
[Value] [Value] [Value] [Value]
377+
378+
应该写的很清楚吧OwO?
379+
```
380+
305381
提示:分配时先分配一个指针数组(`int**` 指向的那一维),然后对每一行分别 `malloc`。释放时顺序反过来——先释放每一行,再释放指针数组本身。
306382

307383
## 参考资源

0 commit comments

Comments
 (0)