Skip to content
This repository has been archived by the owner on Aug 24, 2024. It is now read-only.

Commit

Permalink
Update 05-pointers.md (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
hshq authored Jan 6, 2024
1 parent 59eab65 commit 24c33a0
Showing 1 changed file with 4 additions and 4 deletions.
8 changes: 4 additions & 4 deletions 05-pointers.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Zig 不包含垃圾回收器。管理内存的重任由开发者负责。这是

---

下面的代码创建了一个 `power`1 的用户,然后调用 `levelUp` 函数将用户的 `power` 加一。你能猜到它的输出结果吗?
下面的代码创建了一个 `power`100 的用户,然后调用 `levelUp` 函数将用户的 `power` 加一。你能猜到它的输出结果吗?

```zig
const std = @import("std");
Expand Down Expand Up @@ -60,7 +60,7 @@ user -> ------------ (id)
1. 我们的`user`变量指向结构的起点
2. 字段是按顺序排列的

请记住,我们的`user`也有一个类型。该类型告诉我们 `id` 是一个 64 位整数,`power` 是一个 32 位整数。有了对数据起始位置和类型的引用,编译器就可以将 `user.power` 转换为:访问位置在结构体第 64 位上的一个 32 位整数。这就是变量的威力,它们可以引用内存,并包含以有意义的方式理解和操作内存所需的类型信息。
请记住,我们的`user`也有一个类型。该类型告诉我们 `id` 是一个 64 位整数,`power` 是一个 32 位整数。有了对数据起始位置的引用和类型,编译器就可以将 `user.power` 转换为:访问位置在结构体第 64 位上的一个 32 位整数。这就是变量的威力,它们可以引用内存,并包含以有意义的方式理解和操作内存所需的类型信息。

> 默认情况下,Zig 不保证结构的内存布局。它可以按字母顺序、大小升序或插入填充(padding)某些字段。只要它能正确翻译我们的代码,它就可以为所欲为。这种自由度可以实现某些优化。只有在声明 `packed struct`时,我们才能获得内存布局的有力保证。尽管如此,我们对`user`的可视化还是合理而有用的。
Expand Down Expand Up @@ -132,7 +132,7 @@ fn levelUp(user: User) void {
}
```

如果运行这个程序,会得到两个不同的地址。这意味着在 `levelUp` 中被修改的 `user``main` 中的`user`是不同的。这是因为 Zig 传递了一个值的副本。这似乎是一个奇怪的默认值,但它的好处之一是,函数的调用者可以确保函数不会修改参数(因为它不能)。在很多情况下,有这样的保证是件好事。当然,有时我们希望函数能修改参数,比如 `levelUp`。为此,我们需要 `levelUp` 作用于 `main``user`,而不是其副本。我们可以通过向函数传递 `user`的地址来实现这一点:
如果运行这个程序,会得到两个不同的地址。这意味着在 `levelUp` 中被修改的 `user``main` 中的`user`是不同的。这是因为 Zig 传递了一个值的副本。这似乎是一个奇怪的默认行为,但它的好处之一是,函数的调用者可以确保函数不会修改参数(因为它不能)。在很多情况下,有这样的保证是件好事。当然,有时我们希望函数能修改参数,比如 `levelUp`。为此,我们需要 `levelUp` 作用于 `main``user`,而不是其副本。我们可以通过向函数传递 `user`的地址来实现这一点:

```zig
const std = @import("std");
Expand Down Expand Up @@ -186,7 +186,7 @@ pub const User = struct {

我不止一次暗示过,在默认情况下,Zig 会传递一个值的副本(称为 "按值传递")。很快我们就会发现,实际情况要更微妙一些(提示:嵌套对象的复杂值怎么办?)

即使坚持使用简单类型,事实也是 Zig 可以随心所欲地传递参数,只要它能保证代码的意图不受影响。在我们最初的 `levelUp` 中,参数是一个`User`,Zig 可以传递用户的副本或对 `main.user` 的引用,只要它能保证函数不会对其进行更改即可。(我知道我们最终确实希望它被变异,但通过创建 `User` 类型,我们告诉编译器我们不希望它被变异)。
即使坚持使用简单类型,事实也是 Zig 可以随心所欲地传递参数,只要它能保证代码的意图不受影响。在我们最初的 `levelUp` 中,参数是一个`User`,Zig 可以传递用户的副本或对 `main.user` 的引用,只要它能保证函数不会对其进行更改即可。(我知道我们最终确实希望它被改变,但通过采用 `User` 类型,我们告诉编译器我们不希望它被改变)。

这种自由度允许 Zig 根据参数类型使用最优策略。像 User 这样的小类型可以通过值传递(即复制),成本较低。较大的类型通过引用传递可能更便宜。只要代码的意图得以保留,Zig 可以使用任何方法。在某种程度上,使用常量函数参数可以做到这一点。

Expand Down

0 comments on commit 24c33a0

Please sign in to comment.