Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 112 additions & 1 deletion documents/vol1-fundamentals/ch04/01-pointer-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,80 @@

声明两个 `int` 变量 `a` 和 `b`,打印值和地址,通过指针交换值后再次打印。值变了,地址变了吗?为什么?

::: details 参考答案

```cpp
#include <iostream>
constexpr void swap(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
int a = 10;
int b = 20;
int *p1 = &a;
int *p2 = &b;

std::cout << "========== 交换前 ==========" << std::endl;
std::cout << "变量a的值: " << a << std::endl;
std::cout << "变量a的地址: " << p1 << std::endl;
std::cout << "变量b的值: " << b << std::endl;
std::cout << "变量b的地址: " << p2 << std::endl;

swap(p1, p2);

std::cout << "\n========== 交换后 ==========" << std::endl;
std::cout << "变量a的值: " << a << std::endl;
std::cout << "变量a的地址: " << p1 << std::endl;
std::cout << "变量b的值: " << b << std::endl;
std::cout << "变量b的地址: " << p2 << std::endl;

return 0;
}
```

编译运行:

```bash
g++ -std=c++17 -Wall -Wextra main.cpp -o main &&./main
```

运行结果:

```text
========== 交换前 ==========
变量a的值: 10
变量a的地址: 0x7ffe15dc91b0
变量b的值: 20
变量b的地址: 0x7ffe15dc91b4

========== 交换后 ==========
变量a的值: 20
变量a的地址: 0x7ffe15dc91b0
变量b的值: 10
变量b的地址: 0x7ffe15dc91b4
```

**关键观察:值变了,地址没变**

仔细对比输出会发现:
- `a` 的值从 10 变成了 20,但地址始终是 `0x7ffe15dc91b0`

Check failure on line 366 in documents/vol1-fundamentals/ch04/01-pointer-basics.md

View workflow job for this annotation

GitHub Actions / Markdown Lint

Lists should be surrounded by blank lines

documents/vol1-fundamentals/ch04/01-pointer-basics.md:366 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "- `a` 的值从 10 变成了 20,但地址始终是 `0x..."] https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md032.md
- `b` 的值从 20 变成了 10,但地址始终是 `0x7ffe15dc91b4`

**为什么地址不会变?**

变量的地址在它被创建时就确定了——`int a = 10;` 在栈上分配空间时,操作系统和编译器协作为 `a` 分配了一块 4 字节的内存(假设 `int` 是 4 字节),这块内存的起始位置就是 `a` 的地址。这个地址在变量的整个生命周期内都不会改变。

`swap` 函数通过指针交换的是**变量存储的内容**(即值),而不是变量本身在内存中的位置。就像两个房子交换了住户,但房子的门牌号不会变——`0x7ffe15dc91b0` 这个"门牌号"永远属于变量 `a`,只是里面的"住户"从 10 换成了 20。

这也是指针强大之处的体现:通过地址,我们可以跨越函数边界去修改原始变量的内容,而不需要拷贝整个变量或改变它的内存布局。

:::


### 练习二:追踪指针的值

先不运行,在纸上追踪结果,再编译验证:
Expand All @@ -328,9 +402,26 @@

很多朋友第一次做时会在 `*p = *q` 和 `p = q` 的区别上栽跟头——前者是赋值数据,后者是改变指向。

::: details 参考答案

编译运行:

```bash
g++ -std=c++17 -Wall -Wextra main.cpp -o main &&./main
```

运行结果:

```text

```

:::


### 练习三:修复空指针 bug

下面代码有三个指针相关的 bug,找出并修复:
下面代码有两个指针相关的 bug,找出并修复:

```cpp
#include <iostream>
Expand All @@ -353,6 +444,26 @@
}
```

::: details 参考答案

```cpp

```

编译运行:

```bash
g++ -std=c++17 -Wall -Wextra main.cpp -o main &&./main
```

运行结果:

```text

```

:::

## 小结

这一章从内存地址出发,把指针的核心概念梳理了一遍。`&` 获取地址,指针是存储地址的变量,`*` 解引用指针来读写数据;指针的类型决定了解引用时如何解释内存,但指针本身在 64 位系统上都是 8 字节;`nullptr` 是现代 C++ 表示空指针的正确方式,解引用空指针是未定义行为;`const` 和指针的三种组合控制着数据和指向是否可变;未初始化指针、悬空指针、重复释放是最常见的三类陷阱。
Expand Down
Loading