Skip to content

fix: format heading ids #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 11, 2025
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ zig-cache/
zig-out/

.cache_ggshield
.vscode
old/
8 changes: 4 additions & 4 deletions content/contributing.smd
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文
1. 供稿,分享自己使用 Zig 的心得,方式见下文
2. 改进 zigcc 组织下的开源项目,这是 [open issues](https://ask.ziglang.cc/github)

# [供稿方式]($section.id('contribute'))
# [供稿方式]($heading.id('contribute'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这么改写的优势是?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

原来的 section 不是这样用的,现在弄成 heading 更符合这里的需求和语义。


1. Fork 仓库 https://github.com/zigcc/zigcc.github.io
2. 在 `content/post` 内添加自己的文章(smd格式),文件命名为: `${YYYY}-${MM}-${DD}-${SLUG}.smd`
Expand All @@ -31,20 +31,20 @@ Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文
---
```

# [本地预览]($section.id('本地预览'))
# [本地预览]($heading.id('local-preview'))

首先你得安装 `zine`,安装方法见 [Quick Start](https://zine-ssg.io/quickstart/),之后就可以通过执行 `zine` 命令来预览你的文章。

```bash
zine
```

# [发布平台]($section.id('publishing-platform'))
# [发布平台]($heading.id('publishing-platform'))

- [ZigCC 网站](https://ziglang.cc)
- [ZigCC 公众号](https://github.com/zigcc/.github/raw/main/zig_mp.png)

# [网站更新日志]($section.id('changelog'))
# [网站更新日志]($heading.id('changelog'))

- **2025-06-30:** 切换到 [zine](https://zine-ssg.io/)
- **2024-08-18:** 切换主题 [docsy](https://github.com/google/docsy)
Expand Down
14 changes: 7 additions & 7 deletions content/learn/coding-in-zig.smd
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

在介绍了 Zig 语言的大部分内容之后,我们将对一些主题进行回顾,并展示几种使用 Zig 编程时一些实用的技巧。在此过程中,我们将介绍更多的标准库,并介绍一些稍复杂些的代码片段。

# [悬空指针 Dangling Pointers]($section.id('dangling-pointers'))
# [悬空指针 Dangling Pointers]($heading.id('dangling-pointers'))

我们首先来看看更多关于悬空指针的例子。这似乎是一个奇怪的问题,但如果你之前主要使用带垃圾回收的语言,这可能是你学习 Zig 最大的障碍。

Expand Down Expand Up @@ -105,7 +105,7 @@ const User = struct {

如果把所有东西都放在一个函数中,再加上一个像 `User` 这样的小值,这仍然像是一个人为制造的问题。我们需要一个能让数据所有权成为当务之急的例子。

# [所有权 Ownership]($section.id('ownership'))
# [所有权 Ownership]($heading.id('ownership'))

我喜欢哈希表(HashMap),因为这是每个人都知道并且会经常使用的结构。它们有很多不同的用例,其中大部分你可能都用过。虽然哈希表可以用在一个短期查找的地方,但通常用于长期查找,因此插入其内的值需要同样长的生命周期。

Expand Down Expand Up @@ -218,7 +218,7 @@ defer {

我保证,关于悬挂指针和内存管理的讨论已经结束了。我们所讨论的内容可能还不够清晰或过于抽象。当你有更实际的问题需要解决时,再重新讨论这个问题也不迟。不过,如果你打算编写任何稍具规模(non-trivial)的程序,这几乎肯定是你需要掌握的内容。当你觉得可以的时候,我建议你参考上面这个示例,并自己动手实践一下。引入一个 `UserLookup` 类型来封装我们必须做的所有内存管理。尝试使用 `*User` 代替 `User`,在堆上创建用户,然后像处理键那样释放它们。编写覆盖新结构的测试,使用 `std.testing.allocator` 确保不会泄漏任何内存。

# [ArrayList]($section.id('arraylist'))
# [ArrayList]($heading.id('arraylist'))

现在你可以忘掉我们的 `IntList` 和我们创建的通用替代方案了。Zig 标准库中有一个动态数组实现:`std.ArrayList(T)`。

Expand Down Expand Up @@ -313,7 +313,7 @@ pub fn main() !void {
}
```

# [Anytype]($section.id('anytype'))
# [Anytype]($heading.id('anytype'))

在[语言概述的第一部分](language-overview-1)中,我们简要介绍了 `anytype`。这是一种非常有用的编译时 duck 类型。下面是一个简单的 logger:

Expand Down Expand Up @@ -383,7 +383,7 @@ fn stringify(

第一个参数 `value: anytype` 是显而易见的,它是要序列化的值,可以是任何类型(实际上,Zig 的 JSON 序列化器不能序列化某些类型,比如 HashMap)。我们可以猜测,`out_stream` 是写入 JSON 的地方,但至于它需要实现什么方法,你和我一样猜得到。唯一的办法就是阅读源代码,或者传递一个假值,然后使用编译器错误作为我们的文档。如果有更好的自动文档生成器,这一点可能会得到改善。不过,我希望 Zig 能提供接口,这已经不是第一次了。

# [@TypeOf]($section.id('typeof'))
# [@TypeOf]($heading.id('typeof'))

在前面的部分中,我们使用 `@TypeOf` 来帮助我们检查各种变量的类型。从我们的用法来看,你可能会认为它返回的是字符串类型的名称。然而,鉴于它是一个 PascalCase 风格函数,你应该更清楚:它返回的是一个 `type`。

Expand Down Expand Up @@ -412,7 +412,7 @@ pub const User = struct {

更常见的是 `@TypeOf` 与 `@typeInfo` 配对,后者返回一个 `std.builtin.Type`。这是一个功能强大的带标签的联合(tagged union),可以完整描述一个类型。`std.json.stringify` 函数会递归地调用它,以确定如何将提供的 `value` 序列化。

# [构建系统]($section.id('build-system'))
# [构建系统]($heading.id('build-system'))

如果你通读了整本指南,等待着深入了解如何建立更复杂的项目,包括多个依赖关系和各种目标,那你就要失望了。Zig 拥有强大的构建系统,以至于越来越多的非 Zig 项目都在使用它,比如 libsodium。不幸的是,所有这些强大的功能都意味着,对于简单的需求来说,它并不是最容易使用或理解的。

Expand Down Expand Up @@ -497,7 +497,7 @@ test "dummy build test" {

这是启动和运行构建系统所需的最低配置。但是请放心,如果你需要构建你的程序,Zig 内置的功能大概率能覆盖你的需求。最后,你可以(也应该)在你的项目根目录下使用 `zig init`,让 Zig 为你创建一个文档齐全的 `build.zig` 文件。

# [第三方依赖]($section.id('third-party-dependencies'))
# [第三方依赖]($heading.id('third-party-dependencies'))

Zig 的内置软件包管理器相对较新,因此存在一些缺陷。虽然还有改进的余地,但它目前还是可用的。我们需要了解两个部分:创建软件包和使用软件包。我们将对其进行全面介绍。

Expand Down
18 changes: 9 additions & 9 deletions content/learn/heap-memory.smd
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

本部分分为两个主题。第一个主题是第三个内存区域--堆的总体概述。另一个主题是 Zig 直接而独特的堆内存管理方法。即使你熟悉堆内存,比如使用过 C 语言的 `malloc`,你也会希望阅读第一部分,因为它是 Zig 特有的。

# [堆]($section.id('heap'))
# [堆]($heading.id('heap'))

堆是我们可以使用的第三个也是最后一个内存区域。与全局数据和调用栈相比,堆有点像蛮荒之地:什么都可以使用。具体来说,在堆中,我们可以在运行时创建大小已知的内存,并完全控制其生命周期。

Expand Down Expand Up @@ -52,7 +52,7 @@ fn getRandomCount() !u8 {

一般来说,每次 `alloc` 都会有相应的 `free`。`alloc`分配内存,`free`释放内存。不要让这段简单的代码限制了你的想象力。这种 `try alloc` + `defer free` 的模式很常见,这是有原因的:在我们分配内存的地方附近释放相对来说是万无一失的。但同样常见的是在一个地方分配,而在另一个地方释放。正如我们之前所说,堆没有内置的生命周期管理。你可以在 HTTP 处理程序中分配内存,然后在后台线程中释放,这是代码中两个完全独立的部分。

# [defer 和 errdefer]($section.id('defer-and-errdefer'))
# [defer 和 errdefer]($heading.id('defer-and-errdefer'))

说句题外话,上面的代码介绍了一个新的语言特性:`defer`,它在退出作用域时执行给定的代码。『作用域退出』包括到达作用域的结尾或从作用域返回。严格来说, `defer` 与分配器或内存管理并无严格关系;你可以用它来执行任何代码。但上述用法很常见。

Expand Down Expand Up @@ -100,7 +100,7 @@ pub const Game = struct {

> `init` 和 `deinit` 的名字并不特殊。它们只是 Zig 标准库使用的,也是社区采纳的名称。在某些情况下,包括在标准库中,会使用 `open` 和 `close`,或其他更适当的名称。

# [双重释放和内存泄漏]($section.id('double-free-and-memory-leak'))
# [双重释放和内存泄漏]($heading.id('double-free-and-memory-leak'))

上面提到过,没有规则规定什么时候必须释放什么东西。但事实并非如此,还是有一些重要规则,只是它们不是强制的,需要你自己格外小心。

Expand Down Expand Up @@ -159,7 +159,7 @@ fn isSpecial(allocator: Allocator, name: [] const u8) !bool {

至少在双重释放的情况下,我们的程序会遭遇严重崩溃。内存泄漏可能很隐蔽。不仅仅是根本原因难以确定。真正的小泄漏或不常执行的代码中的泄漏甚至很难被发现。这是一个很常见的问题,Zig 提供了帮助,我们将在讨论分配器时看到。

# [创建与销毁]($section.id('create-and-destroy'))
# [创建与销毁]($heading.id('create-and-destroy'))

`std.mem.Allocator`的`alloc`方法会返回一个切片,其长度为传递的第二个参数。如果想要单个值,可以使用 `create` 和 `destroy` 而不是 `alloc` 和 `free`。

Expand Down Expand Up @@ -233,7 +233,7 @@ fn init(allocator: std.mem.Allocator, id: u64, power: i32) !*User{

请记住,`create` 返回一个 `!*User`,所以我们的 `user` 是 `*User` 类型。

# [分配器 Allocator]($section.id('allocator'))
# [分配器 Allocator]($heading.id('allocator'))

Zig 的核心原则之一是无隐藏内存分配。根据你的背景,这听起来可能并不特别。但这与 C 语言中使用标准库的 malloc 函数分配内存的做法形成了鲜明的对比。在 C 语言中,如果你想知道一个函数是否分配内存,你需要阅读源代码并查找对 malloc 的调用。

Expand All @@ -254,7 +254,7 @@ defer allocator.free(say);

如果你正在构建一个库,那么最好接受一个 `std.mem.Allocator`,然后让库的用户决定使用哪种分配器实现。否则,你就需要选择正确的分配器,正如我们将看到的,这些分配器并不相互排斥。在你的程序中创建不同的分配器可能有很好的理由。

# [通用分配器 GeneralPurposeAllocator]($section.id('general-purpose-allocator'))
# [通用分配器 GeneralPurposeAllocator]($heading.id('general-purpose-allocator'))

顾名思义,`std.heap.GeneralPurposeAllocator` 是一种通用的、线程安全的分配器,可以作为应用程序的主分配器。对于许多程序来说,这是唯一需要的分配器。程序启动时,会创建一个分配器并传递给需要它的函数。我的 HTTP 服务器库中的示例代码就是一个很好的例子:

Expand Down Expand Up @@ -299,7 +299,7 @@ var gpa = std.heap.GeneralPurposeAllocator(.{}){};

类型是什么,字段在哪里?类型其实是 `std.heap.general_purpose_allocator.Config`,但它并没有直接暴露出来,这也是我们没有显式给出类型的原因之一。没有设置字段是因为 Config 结构定义了默认值,我们将使用默认值。这是配置、选项的中常见的模式。事实上,我们在下面几行向 `init` 传递 `.{.port = 5882}` 时又看到了这种情况。在本例中,除了端口这一个字段外,我们都使用了默认值。

# [std.testing.allocator]($section.id('std-testing-allocator'))
# [std.testing.allocator]($heading.id('std-testing-allocator'))

希望当我们谈到内存泄漏时,你已经足够烦恼,而当我提到 Zig 可以提供帮助时,你肯定渴望了解更多这方面内容。这种帮助来自 `std.testing.allocator`,它是一个 `std.mem.Allocator` 实现。目前,它基于通用分配器(GeneralPurposeAllocator)实现,并与 Zig 的测试运行器进行了集成,但这只是实现细节。重要的是,如果我们在测试中使用 `std.testing.allocator`,就能捕捉到大部分内存泄漏。

Expand Down Expand Up @@ -423,7 +423,7 @@ self.allocator.free(self.items);

将`items`复制到我们的 `larger` 切片中后, 添加最后一行`free`可以解决泄漏的问题。如果运行 `zig test learning.zig`,便不会再有错误。

# [ArenaAllocator]($section.id('arena-allocator'))
# [ArenaAllocator]($heading.id('arena-allocator'))

通用分配器(GeneralPurposeAllocator)是一个合理的默认设置,因为它在所有可能的情况下都能很好地工作。但在程序中,你可能会遇到一些固定场景,使用更专业的分配器可能会更合适。其中一个例子就是需要在处理完成后丢弃的短期状态。解析器(Parser)通常就有这样的需求。一个 `parse` 函数的基本轮廓可能是这样的

Expand Down Expand Up @@ -508,7 +508,7 @@ defer list.deinit();

最后举个简单的例子,我上面提到的 HTTP 服务器在响应中暴露了一个 `ArenaAllocator`。一旦发送了响应,它就会被清空。由于`ArenaAllocator`的生命周期可以预测(从请求开始到请求结束),因此它是一种高效的选择。就性能和易用性而言,它都是高效的。

# [固定缓冲区分配器 FixedBufferAllocator]($section.id('fixed-buffer-allocator'))
# [固定缓冲区分配器 FixedBufferAllocator]($heading.id('fixed-buffer-allocator'))

我们要讨论的最后一个分配器是 `std.heap.FixedBufferAllocator`,它可以从我们提供的缓冲区(即 `[]u8`)中分配内存。这种分配器有两大好处。首先,由于所有可能使用的内存都是预先创建的,因此速度很快。其次,它自然而然地限制了可分配内存的数量。这一硬性限制也可以看作是一个缺点。另一个缺点是,`free` 和 `destroy` 只对最后分配/创建的项目有效(想想堆栈)。调用释放非最后分配的内存是安全的,但不会有任何作用。

Expand Down
10 changes: 5 additions & 5 deletions content/learn/index.smd
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
.draft = false,
---

# [《学习 Zig》 目录]($section.id('table-of-contents'))
# [《学习 Zig》 目录]($heading.id('table-of-contents'))

- [前言](./preface)
- [安装 Zig](./installing-zig)
Expand All @@ -24,7 +24,7 @@

初次接触 Zig 的用户可以按序号依次阅读,对于有经验的 Zig 开发者可按需阅读感兴趣的章节。

# [关于原作者]($section.id('about-original-author'))
# [关于原作者]($heading.id('about-original-author'))

[Karl Seguin](https://www.linkedin.com/in/karlseguin/) 在多个领域有着丰富经验,前微软 MVP,他撰写了大量文章,是多个微软公共新闻组的活跃成员。现居新加坡。他还是以下教程的作者:

Expand All @@ -34,17 +34,17 @@

可以在 <http://openmymind.net> 找到他的博客,或者通过 [@karlseguin](http://twitter.com/karlseguin) 在 Twitter 上关注他。

# [翻译原则]($section.id('translation-principles'))
# [翻译原则]($heading.id('translation-principles'))

技术文档的翻译首要原则是准确,但在准确的前提下如何保证『信、达、雅』?这是个挑战,在翻译本教程时,在某些情况下会根据上下文进行意译,便于中文读者阅读。

最后,感谢翻译者的无私贡献。❤️️

# [离线阅读]($section.id('offline-reading'))
# [离线阅读]($heading.id('offline-reading'))

在本仓库的 [release 页面](https://github.com/zigcc/zigcc.github.io/releases)会定期将本教程导出为 PDF 格式,读者可按需下载。

# [其他学习资料]($section.id('other-learning-resources'))
# [其他学习资料]($heading.id('other-learning-resources'))

由于 Zig 目前还处于快速迭代,因此最权威的资料无疑是官方的 [Zig Language Reference](https://ziglang.org/documentation/master/),遇到语言的细节问题,基本都可以在这里找到答案。其次是社区的一些高质量教程,例如:

Expand Down
14 changes: 7 additions & 7 deletions content/learn/language-overview-1.smd
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub const User = struct {

> 请参阅[安装 Zig 部分](installing-zig),以便快速启动并运行它。

# [模块引用]($section.id('module-reference'))
# [模块引用]($heading.id('module-reference'))

很少有程序是在没有标准库或外部库的情况下以单个文件编写的。我们的第一个程序也不例外,它使用 Zig 的标准库来进行打印输出。 Zig 的模块系统非常简单,只依赖于 `@import` 函数和 `pub` 关键字(使代码可以在当前文件外部访问)。

Expand Down Expand Up @@ -88,7 +88,7 @@ const MAX_POWER = user.MAX_POWER;
- 如何导入其他文件
- 如何导出变量、函数定义

# [代码注释]($section.id('code-comment'))
# [代码注释]($heading.id('code-comment'))

下面这行 Zig 代码是一个注释:

Expand All @@ -100,7 +100,7 @@ Zig 没有像 C 语言中类似 `/* ... */` 的多行注释。

基于注释的文档自动生成功能正在试验中。如果你看过 Zig 的标准库文档,你就会看到它的实际应用。`//!` 被称为顶级文档注释,可以放在文件的顶部。三斜线注释 (`///`) 被称为文档注释,可以放在特定位置,如声明之前。如果在错误的地方使用这两种文档注释,编译器都会出错。

# [函数]($section.id('function'))
# [函数]($heading.id('function'))

下面这行 Zig 代码是程序的入口函数 `main`:

Expand Down Expand Up @@ -146,7 +146,7 @@ fn add(a: i64, b: i64) i64 {

为了提高可读性,Zig 中不支持函数重载(用不同的参数类型或参数个数定义的同名函数)。暂时来说,以上就是我们需要了解的有关函数的全部内容。

# [结构体]($section.id('struct'))
# [结构体]($heading.id('struct'))

下面这行代码创建了一个 `User` 结构体:

Expand Down Expand Up @@ -266,7 +266,7 @@ pub fn init(name: []const u8, power: u64) User {

就像我们迄今为止已经探索过的大多数东西一样,今后在讨论 Zig 语言的其他部分时,我们会再次讨论结构体。不过,在大多数情况下,它们都是简单明了的。

# [数组和切片]($section.id('array-slice'))
# [数组和切片]($heading.id('array-slice'))

我们可以略过代码的最后一行,但鉴于我们的代码片段包含两个字符串 `"Goku"` 和 `{s}'s power is {d}\n`,你可能会对 Zig 中的字符串感到好奇。为了更好地理解字符串,我们先来了解一下数组和切片。

Expand Down Expand Up @@ -365,7 +365,7 @@ pub fn main() void {

在了解 Zig 语言的其他方面(尤其是字符串)的同时,我们还将发现更多有关数组和切片的知识。

# [字符串]($section.id('string'))
# [字符串]($heading.id('string'))

我希望我能说,Zig 里有字符串类型,而且非常棒。遗憾的是,它没有。最简单来说,字符串是字节(u8)的序列(即数组或切片)。实际上,我们可以从 `name` 字段的定义中看到这一点:`name: []const u8`.

Expand Down Expand Up @@ -399,7 +399,7 @@ pub fn main() void {

当然,在实际程序中,大多数字符串(以及更通用的数组)在编译时都是未知的。最典型的例子就是用户输入,程序编译时并不知道用户输入。这一点我们将在讨论内存时再次讨论。但简而言之,对于这种在编译时不能确定值的数据(长度当然也就无从得知),我们将在运行时动态分配内存。我们的字符串变量(仍然是 `[]const u8` 类型)将是指向动态分配的内存的切片。

# [comptime 和 anytype]($section.id('comptime-and-anytype'))
# [comptime 和 anytype]($heading.id('comptime-and-anytype'))

在我们未解释的最后一行代码中,涉及的知识远比表面看到的多:

Expand Down
Loading