Skip to content

Commit 992c7c4

Browse files
committed
对 C 语言系统教程第四章第五章提供了参考答案
1 parent 238fabf commit 992c7c4

2 files changed

Lines changed: 124 additions & 23 deletions

File tree

documents/vol1-fundamentals/c_tutorials/03A-operators-basics.md

Lines changed: 51 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: 掌握 C 语言的算术运算符、自增自减、关系与逻辑运算符、条件运算符和逗号运算符,理解短路求值和赋值运算符的用法
66
difficulty: beginner
77
order: 4
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
上一篇里我们把 C 语言的数据类型从里到外拆了一遍——整数怎么存、小数怎么存、字符怎么存。但光有数据还不够,我们还得让数据"动起来":做加减乘除、比较大小、判断真假。这些操作在 C 语言里由**运算符**来完成。
@@ -35,7 +36,7 @@ title: 运算符基础:让数据动起来
3536

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

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

@@ -266,6 +267,16 @@ printf("%d\n", 7 % 2);
266267
printf("%d\n", -7 % 2);
267268
```
268269
270+
### 练习 1 参考答案
271+
272+
```text
273+
3 // 7 / 2 得 3.5 ,正整型向下取整,得3
274+
-3 // -7 / 2 得 -3.5,负整型向上取整,得3
275+
-3 // 其实我觉得不论是正整形还是负整形,都是向0取整。比如:7 / -2 得 -3.5 , 然后向0取整,得-3
276+
1 // 7 % 2 得 1 (这应该不用过多做解释吧)
277+
-1 // -7 % 2 等于 -(7 % 2) 等于 -(1),所以,得:-7 % 2 等于 -1
278+
```
279+
269280
### 练习 2:短路求值实战
270281

271282
写一个函数,安全地从数组中找到第一个大于指定值的元素。利用短路求值确保不越界:
@@ -279,6 +290,38 @@ printf("%d\n", -7 % 2);
279290
int find_first_above(const int* arr, size_t len, int threshold);
280291
```
281292
293+
### 练习 2 参考答案
294+
295+
```c
296+
#include <stddef.h>
297+
298+
int find_first_above(const int* arr, size_t len, int threshold) {
299+
// 边界情况:如果数组为空指针或长度为0,直接返回 -1
300+
if (arr == NULL || len == 0) {
301+
return -1;
302+
}
303+
304+
size_t i = 0;
305+
306+
// 核心逻辑:利用短路求值防止越界
307+
// 1. 首先检查 i < len
308+
// 2. 只有当 i < len 为真时,才会去访问 arr[i] (避免越界读取)
309+
// 3. 如果 arr[i] <= threshold,循环继续
310+
while (i < len && arr[i] <= threshold) {
311+
i++;
312+
}
313+
314+
// 循环退出有两个原因:
315+
// 1. 找到了大于 threshold 的元素 (此时 i < len)
316+
// 2. 遍历完了数组也没找到 (此时 i == len)
317+
if (i < len) {
318+
return (int)i;
319+
} else {
320+
return -1;
321+
}
322+
}
323+
```
324+
282325
## 参考资源
283326

284327
- [cppreference: C 语言运算符优先级](https://en.cppreference.com/w/c/language/operator_precedence)

documents/vol1-fundamentals/c_tutorials/03B-bitwise-and-evaluation.md

Lines changed: 73 additions & 15 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: 深入位运算的四大操作、移位注意事项、运算符优先级陷阱、求值顺序与序列点,理解未定义行为的本质
66
difficulty: beginner
77
order: 5
88
platform: host
99
prerequisites:
10-
- 运算符基础:让数据动起来
10+
- 运算符基础:让数据动起来
1111
reading_time_minutes: 10
1212
tags:
13-
- host
14-
- cpp-modern
15-
- beginner
16-
- 入门
13+
- host
14+
- cpp-modern
15+
- beginner
16+
- 入门
1717
title: 位运算与求值顺序
1818
---
19+
1920
# 位运算与求值顺序
2021

2122
上一篇里我们把算术、关系、逻辑这些常用运算符过了一遍。现在我们来啃两块比较硬的骨头:位运算和求值顺序。位运算在一般的应用层编程中用得不多,但如果你以后要接触嵌入式开发或者底层系统编程,位运算就是你的日常工具——配置硬件寄存器、解析通信协议的位字段、实现标志位集合,全靠它。求值顺序和序列点则是理解"为什么有些代码在不同编译器上结果不一样"的关键。
@@ -34,7 +35,7 @@ title: 位运算与求值顺序
3435

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

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

@@ -46,14 +47,14 @@ title: 位运算与求值顺序
4647

4748
C 提供了六种位运算符:
4849

49-
| 运算符 | 含义 | 简单理解 |
50-
|--------|------|---------|
51-
| `&` | 按位与 | 两个都是 1 才得 1 |
52-
| `\|` | 按位或 | 有一个 1 就得 1 |
53-
| `^` | 按位异或 | 不同得 1,相同得 0 |
54-
| `~` | 按位取反 | 0 变 1,1 变 0 |
55-
| `<<` | 左移 | 所有位向左移动,低位补 0 |
56-
| `>>` | 右移 | 所有位向右移动,高位补 0(无符号数) |
50+
| 运算符 | 含义 | 简单理解 |
51+
| ------ | -------- | ------------------------------------ |
52+
| `&` | 按位与 | 两个都是 1 才得 1 |
53+
| `\|` | 按位或 | 有一个 1 就得 1 |
54+
| `^` | 按位异或 | 不同得 1,相同得 0 |
55+
| `~` | 按位取反 | 0 变 1,1 变 0 |
56+
| `<<` | 左移 | 所有位向左移动,低位补 0 |
57+
| `>>` | 右移 | 所有位向右移动,高位补 0(无符号数) |
5758

5859
我们用 8 位无符号数来演示,这样比较直观:
5960

@@ -312,6 +313,39 @@ uint32_t bit_toggle(uint32_t value, int n);
312313
uint32_t bit_extract(uint32_t value, int high, int low);
313314
```
314315
316+
### 练习 1 的参考答案
317+
318+
```c
319+
320+
/// @brief 将 value 的第 n 位置为 1
321+
uint32_t bit_set(uint32_t value, int n) {
322+
value = value | (1U << n);
323+
return value;
324+
}
325+
326+
/// @brief 将 value 的第 n 位清零
327+
uint32_t bit_clear(uint32_t value, int n) {
328+
value = value & (~(1U << n));
329+
return value;
330+
}
331+
332+
/// @brief 翻转 value 的第 n 位
333+
uint32_t bit_toggle(uint32_t value, int n) {
334+
value = value ^ (1U << n);
335+
return value;
336+
}
337+
338+
/// @brief 提取 value 的 [high:low] 位域(包含两端)
339+
uint32_t bit_extract(uint32_t value, int high, int low) {
340+
uint32_t width = high - low + 1;
341+
value = value >> low;
342+
uint64_t mask = (1ULL << width) - 1;
343+
value = value & mask;
344+
return value;
345+
}
346+
347+
```
348+
315349
### 练习 2:安全的移位
316350

317351
写一个函数,安全地执行左移操作,处理所有边界情况:
@@ -325,6 +359,20 @@ uint32_t bit_extract(uint32_t value, int high, int low);
325359
uint32_t safe_shift_left(uint32_t val, int n, int bits);
326360
```
327361
362+
### 练习 2 的参考答案
363+
364+
```c
365+
uint32_t safe_shift_left(uint32_t val, int n, int bits) {
366+
if (bits <= 0 || bits > 32) {
367+
return 0;
368+
}
369+
if (n < 0 || n >= bits) {
370+
return 0;
371+
}
372+
return val << n;
373+
}
374+
```
375+
328376
### 练习 3:表达式分析
329377

330378
分析以下表达式的求值行为(不实际运行),标出每个是"明确定义"、"未指定行为"还是"未定义行为":
@@ -337,6 +385,16 @@ int r3 = (a > b) ? a-- : b--; // ?
337385
printf("%d %d\n", a++, a++); // ?
338386
```
339387
388+
### 练习 3 的参考答案
389+
390+
```c
391+
int a = 5, b = 3;
392+
int r1 = a++ + b; // 明确定义
393+
int r2 = a++ + ++a; // UB
394+
int r3 = (a > b) ? a-- : b--; // 明确定义
395+
printf("%d %d\n", a++, a++); // UB
396+
```
397+
340398
## 参考资源
341399

342400
- [cppreference: C 运算符优先级](https://en.cppreference.com/w/c/language/operator_precedence)

0 commit comments

Comments
 (0)