diff --git a/course/advanced/atomic.md b/course/advanced/atomic.md index 9588a6b8..d26c9e82 100644 --- a/course/advanced/atomic.md +++ b/course/advanced/atomic.md @@ -15,21 +15,16 @@ outline: deep 在讲述下列的内建函数前,我们需要了解一下前置知识: -**原子操作的顺序级别**:为了实现性能和必要保证之间的平衡,原子性分为六个级别。它们按照强度顺序排列,每个级别都包含上一个级别的所有保证。 +**原子操作的顺序级别(Memory Ordering)**:为了实现性能和必要保证之间的平衡,原子操作提供了不同的内存排序级别。它们按照约束强度从弱到强排列: -关于原子顺序六个级别的具体说明,见 [LLVM](https://llvm.org/docs/Atomics.html#atomic-orderings)。 +- **Unordered**:最弱的原子保证。仅保证操作本身是原子的(不会被撕裂),但不提供任何跨线程的顺序保证。 +- **Monotonic**(对应 C++ 的 `memory_order_relaxed`):保证同一线程内对同一变量的原子操作是单调有序的,但不阻止不同变量之间的操作重排序。适用于简单的计数器等场景。 +- **Acquire**:读操作使用。保证当前线程在此读操作**之后**的所有读写操作,不会被重排到此操作之前。常用于获取锁。 +- **Release**:写操作使用。保证当前线程在此写操作**之前**的所有读写操作,不会被重排到此操作之后。常用于释放锁。 +- **AcqRel**(Acquire + Release):同时具备 Acquire 和 Release 语义,适用于读 - 改-写(Read-Modify-Write)操作。 +- **SeqCst**(Sequentially Consistent):最强的保证。除了包含 AcqRel 的所有保证外,还保证所有线程观察到的 SeqCst 操作的顺序是一致的。开销最大,但最易于推理。 - +关于更多细节,见 [LLVM Atomics](https://llvm.org/docs/Atomics.html#atomic-orderings)。 ### [`@atomicLoad`](https://ziglang.org/documentation/master/#atomicLoad) diff --git a/course/advanced/comptime.md b/course/advanced/comptime.md index d166a8fb..24618e3e 100644 --- a/course/advanced/comptime.md +++ b/course/advanced/comptime.md @@ -6,11 +6,11 @@ outline: deep 在开始之前,我们需要先梳理一下,什么是**编译期**? -对于这个概念,你可能会一脸懵逼。所以我们先看一下什么是运行时(runtime): +要理解编译期,我们先回顾一下**运行时(Runtime)**的概念: -> “在计算机科学中代表一个计算机程序从开始执行到终止执行的运作、执行的时期。” +> “在计算机科学中,运行时代表一个计算机程序从开始执行到终止执行的运作、执行的时期。” -对应地,我们可以尝试给编译期做一个定义:“zig 编译期是指在 zig 编译期间执行的动作。” +对应地,**编译期求值(Compile-time Evaluation)** 是指在编译阶段——即源代码被翻译为机器码的过程中——对表达式进行求值的机制。与运行时求值不同,编译期求值的结果会被直接嵌入到最终的二进制产物中,不产生任何运行时开销。 :::info 🅿️ 提示 @@ -25,9 +25,11 @@ outline: deep - 在这个调用点,标记的值必须是在编译期已知的,否则 zig 会报告错误! - 在函数定义中,该值(包括参数、类型)必须是编译期已知的(但无需全部都是编译期已知的,仅保证依赖关系中的符合即可)! -## 编译期参数实现鸭子类型 +## 编译期参数实现泛型(参数多态) -> “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” +> "当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。" + +上面这句话描述的是动态语言中的"鸭子类型"。Zig 的编译期泛型与之类似但有本质区别:Zig 是在**编译期**通过参数多态(Parametric Polymorphism)来实现泛型的——类型作为编译期参数传入,编译器会为每个具体类型生成特化的代码,所有类型检查都在编译期完成。 一个实现 `max` 功能的函数: diff --git a/course/advanced/interact-with-c.md b/course/advanced/interact-with-c.md index 610f0a9a..9a2352ad 100644 --- a/course/advanced/interact-with-c.md +++ b/course/advanced/interact-with-c.md @@ -176,7 +176,7 @@ pub const MAKELOCAL = 应尽量避免使用此类型,通常它仅出现在翻译输出代码中。 -导入 C 头文件后,zig 并不知道如何处理指针(因为 C 的指针可以同时作为单项指针和多项指针使用),这会导致歧义,故 zig 引入一种新类型 `[*c]T`,作为一种折中方案,新类型 `[*c]T` 具有以下特点: +导入 C 头文件后,Zig 并不知道如何处理指针(因为 C 的指针可以同时作为单项指针和多项指针使用),这会导致歧义。为此,Zig 引入了 `[*c]T` 类型作为**兼容性退化机制**——它在类型信息不完整时保持与 C 的互操作性,同时允许开发者后续将其转换为更精确的 Zig 指针类型。`[*c]T` 具有以下特点: 1. 支持 zig 普通指针(`*T` 和 `[*]T`)的全部语法。 2. 可以强制转换为其他的任意指针类型,当然也包括可选指针类型(当被转换为非可选指针时,如果地址为 0,此时会触发安全检查的保护机制,报错并通知出现了未定义行为)。 diff --git a/course/advanced/memory_manage.md b/course/advanced/memory_manage.md index b8b559cb..50549b2a 100644 --- a/course/advanced/memory_manage.md +++ b/course/advanced/memory_manage.md @@ -18,7 +18,7 @@ outline: deep 6. [`page_allocator`](https://ziglang.org/documentation/master/std/#std.heap.page_allocator) 7. [`StackFallbackAllocator`](https://ziglang.org/documentation/master/std/#std.heap.StackFallbackAllocator) -除了这八种内存分配模型外,还提供了内存池的功能 [`MemoryPool`](https://ziglang.org/documentation/master/std/#std.heap.memory_pool.MemoryPool) +除了这七种内存分配模型外,还提供了内存池的功能 [`MemoryPool`](https://ziglang.org/documentation/master/std/#std.heap.memory_pool.MemoryPool) 你可能对上面的多种内存模型感到很迷惑,C 语言中不就是 `malloc` 吗,怎么到这里这么多的“模型”,这些模型均有着不同的特点,而且它们之间有一部分还可以叠加使用,Zig 在这方面提供了更多的选择,而且不仅仅是这些,你还可以自己尝试实现一个内存模型。 diff --git a/course/advanced/reflection.md b/course/advanced/reflection.md index b8130df9..659f8b66 100644 --- a/course/advanced/reflection.md +++ b/course/advanced/reflection.md @@ -4,9 +4,11 @@ outline: deep # 反射 -> 在计算机学中,反射(**reflection**),是指计算机程序在运行时(**runtime**)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。 +> 在计算机科学中,反射(**Reflection**)是指程序在执行过程中可以访问、检测和修改自身结构或行为的一种能力。 -事实上,由于 zig 是一门强类型的静态语言,因此它的反射是在编译期实现的,允许我们观察已有的类型,并根据已有类型的信息来创造新的类型! +反射通常包含两个层面:**内省(Introspection)**——即程序检查自身类型信息的能力;以及**中间表示操纵(Intercession)**——即程序修改自身结构的能力。 + +在传统的动态语言(如 Python、Ruby)中,反射发生在运行时。而 Zig 是一门强类型的静态语言,它的反射完全在**编译期**实现——我们可以在编译期观察已有类型的信息(内省),并根据这些信息构建全新的类型(有限度的中间表示操纵)。这种编译期反射的优势在于:不会引入任何运行时开销,且所有类型错误都能在编译期被捕获。 ## 观察已有类型 diff --git a/course/advanced/type_cast.md b/course/advanced/type_cast.md index 243ee4c1..ed7465a4 100644 --- a/course/advanced/type_cast.md +++ b/course/advanced/type_cast.md @@ -126,7 +126,7 @@ undefined 是一个神奇的值,它可以赋值给所有类型,代表这个 ## 对等类型转换 -对等类型转换(**Peer Type Resolution**),这个词汇仅仅在 zig 的文档中出现过,它看起来与前面提到的普通类型解析很像,根据 zig 的[开发手册](https://ziglang.org/documentation/master/)所述,它发生在以下情况: +对等类型转换(**Peer Type Resolution**)是 Zig 类型系统中的一种类型推断机制。其核心思想是:当多个表达式需要产生统一类型的结果时,编译器会自动寻找它们的**最小公共超类型(Least Upper Bound)**——即能够无损表示所有参与类型的最小类型。根据 Zig 的[开发手册](https://ziglang.org/documentation/master/)所述,它发生在以下情况: - `switch` 的表达式 - `if` 的表达式 diff --git a/course/basic/advanced_type/pointer.md b/course/basic/advanced_type/pointer.md index d07ecd58..65ef09fd 100644 --- a/course/basic/advanced_type/pointer.md +++ b/course/basic/advanced_type/pointer.md @@ -116,7 +116,7 @@ Zig 支持指针的加减运算,但建议在进行运算前,将指针转换 ::: -## 多项指针和单向指针区别 +## 多项指针和单项指针区别 本节专门解释单项指针和多项指针的区别。 diff --git a/course/basic/advanced_type/slice.md b/course/basic/advanced_type/slice.md index 424ee983..57691471 100644 --- a/course/basic/advanced_type/slice.md +++ b/course/basic/advanced_type/slice.md @@ -32,7 +32,7 @@ slice_2 类型为[]i32 在上面的例子中,我们从一个数组创建切片,其左边界为 0,右边界为变量 `len`。 -注意:如果切片的两个边界值都是编译期常量,编译器会将其优化为数组指针;但如果至少有一个边界值是运行时变量,那么它就是一个真正的切片。 +注意:如果切片的两个边界值都是编译期常量,编译器在类型推断时会将结果推断为数组指针类型(因为长度在编译期已知);但如果至少有一个边界值是运行时变量,那么结果类型就是切片(长度在运行时确定)。 :::info 🅿️ 提示 diff --git a/course/basic/advanced_type/string.md b/course/basic/advanced_type/string.md index 9ad5583e..da563268 100644 --- a/course/basic/advanced_type/string.md +++ b/course/basic/advanced_type/string.md @@ -50,7 +50,7 @@ Unicode 码点字面量类型是 `comptime_int`。所有转义字符都可以在 :::info 字符串字面量中不能直接包含``字符(Zig 语言规范不允许在源代码中使用``)。但可以使用`\t`转义序列或`@embedFile`内建函数来实现类似的功能。 -参考:[enum-backed address spaces](https://github.com/ziglang/zig-spec/issues/38] +参考:[enum-backed address spaces](https://github.com/ziglang/zig-spec/issues/38) ::: diff --git a/course/basic/advanced_type/struct.md b/course/basic/advanced_type/struct.md index bac7b519..46db9515 100644 --- a/course/basic/advanced_type/struct.md +++ b/course/basic/advanced_type/struct.md @@ -137,7 +137,7 @@ 然而,仅此还不够。在实际使用中,我们可能只初始化部分字段,而其他字段使用默认值。如果对结构体字段的默认值没有不变性要求,那么这种默认值方案已经足够使用。 -但如果要求结构体字段的值具有默认不变性(即要么全部使用默认值,要么全部由使用者手动赋值),则可以采用以下方案: +但如果需要"全有或全无"(All-or-Nothing)的初始化策略——即要么全部使用默认值,要么全部由使用者手动赋值——则可以采用以下方案: <<<@/code/release/struct.zig#all_default diff --git a/course/basic/basic_type/function.md b/course/basic/basic_type/function.md index ca2cd580..182958eb 100644 --- a/course/basic/basic_type/function.md +++ b/course/basic/basic_type/function.md @@ -26,7 +26,7 @@ Zig 的函数结构清晰,你可以一眼看出其组成部分。我们来用 <<<@/code/release/function.zig#max -其中 `comptime T: type` 可能对你来说比较陌生,这是[编译期](../../advanced/comptime.md)参数,它是实现鸭子类型(泛型)的关键语法。 +其中 `comptime T: type` 可能对你来说比较陌生,这是[编译期](../../advanced/comptime.md)参数,它是实现泛型(参数多态)的关键语法。 ::: @@ -74,21 +74,11 @@ Zig 在这方面的处理是:原始类型(如整型、布尔)完全使用 以下是一些更加高级的用法,可以之后再学习! -### 闭包 +### 模拟闭包模式 -首先,我们来看维基百科对闭包的定义: +在计算机科学中,**闭包(Closure)** 是指一个函数与其引用的词法作用域(Lexical Scope)形成的组合——闭包允许一个函数访问其外部作用域中的变量,即使这个函数在其外部作用域之外被调用。其主要特性是能够“记住”其创建时的环境。 -> 在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持头等函数的编程语言中实现词法绑定的一种技术。 -> -> 闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。 -> -> 环境里是若干对符号和值的对应关系,它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。 -> -> 捕捉时对于值的处理可以是值拷贝,也可以是名称引用,这通常由语言设计者决定,也可能由用户自行指定(如 C++)。 - -在 Zig 中,由于语言设计上的某些限制,我们无法像在某些其他语言中那样自由地使用闭包特性。 - -广义上讲,**闭包是指一个函数与其引用的词法作用域(lexical scope)形成的组合。简言之,闭包允许一个函数访问其外部作用域中的变量,即使这个函数在其外部作用域之外被调用。其主要特性是能够“记住”其创建时的环境。** +Zig 语言不允许在函数内部声明函数,也不允许直接创建匿名函数,因此 Zig **不支持传统意义上的闭包**(即捕获运行时自由变量的函数)。但我们可以通过编译期参数来**模拟**类似闭包的效果——需要注意的是,这种模式只能捕获编译期已知的值,与真正的闭包有本质区别。 在许多支持内存垃圾回收(GC)的语言中,它们的使用大概是这样的: @@ -108,11 +98,9 @@ closure = outer_function() closure() # 输出:Hello, World! ``` -以上是一段 Python 代码。其中 `outer_function` 函数最终返回一个函数类型(实际上它返回了 `inner_function` 函数,但在解释执行时,它不再以 `inner_function` 的名称存在)。 - -Zig 语言不允许在函数内部声明函数,也不允许直接创建匿名函数。这两个特性在其他编程语言(例如 JavaScript、PowerQuery-M 等)中是实现闭包模式的常见方式。 +以上是一段 Python 代码,其中 `inner_function` 捕获了外部函数的局部变量 `message`,这是典型的闭包行为。 -因此,在 Zig 中实现闭包,通常需要其在编译期是已知的: +在 Zig 中模拟类似效果,通常需要借助编译期参数: <<<@/code/release/function.zig#closure diff --git a/course/basic/basic_type/number.md b/course/basic/basic_type/number.md index 40389f39..1b0e5522 100644 --- a/course/basic/basic_type/number.md +++ b/course/basic/basic_type/number.md @@ -142,7 +142,7 @@ thread 2456131 panic: division by zero 值得注意的是,`comptime_float` 具有 `f128` 的精度和运算能力。 -浮点字面量可以隐式转换为**任意浮点类型**。如果浮点字面量没有小数部分,它还可以隐式转换为**任意整数类型**。 +编译期已知的浮点字面量可以隐式转换为精度足以表示该值的浮点类型。如果浮点字面量没有小数部分,它还可以隐式转换为范围足以容纳该值的整数类型。 浮点运算默认遵循 `Strict` 模式,但可以使用 `@setFloatMode(.Optimized)` 切换到 `Optimized` 模式。有关浮点运算模式的详细信息,请参见 [`@setFloatMode`](https://ziglang.org/documentation/master/#setFloatMode)。 @@ -179,9 +179,7 @@ pub fn main() void { > 常见的加减乘除运算在此不再赘述,我们来聊聊 Zig 中独具特色的一些操作符。 - - -- `+|`:饱和加法。这涉及到[对等类型解析](../../advanced/type_cast.md#对等类型转换)。简单来说,加法结果不会超过该类型的最大值。例如,`u8` 类型的 255 加 1 后仍然是 255。 +- `+|`:饱和加法。加法结果不会超过该类型的最大值。例如,`u8` 类型的 255 加 1 后仍然是 255。涉及多个操作数时,类型由[对等类型转换](../../advanced/type_cast.md#对等类型转换)规则决定。 - `-|`:饱和减法。与饱和加法类似,减法结果不会低于该类型的最小值。 - `*|`:饱和乘法。乘法结果不会超过该类型的最大值或最小值。 - `<<|`:饱和左移。左移结果不会超过该类型的最大值。 diff --git a/course/basic/define-variable.md b/course/basic/define-variable.md index f3b8805b..861680ff 100644 --- a/course/basic/define-variable.md +++ b/course/basic/define-variable.md @@ -8,7 +8,7 @@ outline: deep ## 变量声明 -> 变量是用于在内存中存储值的命名空间。 +> 变量是与一个标识符(名称)绑定的、用于在内存中存储值的存储位置。 在 Zig 中,我们使用 `var` 关键字来声明变量,其格式为 `var variable_name: Type = initial_value;`。以下是一个示例: diff --git a/course/basic/error_handle.md b/course/basic/error_handle.md index 254734b4..cfd5d9f1 100644 --- a/course/basic/error_handle.md +++ b/course/basic/error_handle.md @@ -46,7 +46,7 @@ outline: deep `anyerror` 指的是全局错误集,它包含编译单元中的所有错误,是所有其他错误集的超集。 -任何错误集都可以隐式转换为全局错误集,但反之则不然。从 `anyerror` 到其他错误集的转换需要显式进行,此时会增加一个语言级断言(language-level assert),要求该错误一定在目标错误集中存在。 +从类型系统的角度看,任何具体的错误集都是 `anyerror` 的**子类型**。因此,任何错误集都可以隐式转换(向上转型)为 `anyerror`,但反之则不然——从 `anyerror` 到具体错误集的转换需要显式进行,此时编译器会增加一个语言级断言(language-level assert),要求该错误确实存在于目标错误集中,否则会触发安全检查错误。 ::: warning ⚠️ 警告 diff --git a/course/basic/optional_type.md b/course/basic/optional_type.md index d0a043c9..1a35ad10 100644 --- a/course/basic/optional_type.md +++ b/course/basic/optional_type.md @@ -4,7 +4,7 @@ outline: deep # 可选类型 -## Overview +## 概述 在 Zig 中,为了在不损害效率的前提下提高代码安全性,可选类型是一个重要的解决方案。它的标志是 `?`,`?T` 表示该类型的值可以是 `null` 或 `T` 类型。 diff --git a/course/basic/process_control/defer.md b/course/basic/process_control/defer.md index 7e2d6d17..2316eeb2 100644 --- a/course/basic/process_control/defer.md +++ b/course/basic/process_control/defer.md @@ -4,12 +4,38 @@ outline: deep # defer -`defer` 将在当前作用域末尾执行表达式。 +`defer` 用于注册一个表达式(或代码块),使其在当前作用域结束时自动执行。这是 Zig 提供的一种**确定性资源管理**机制,功能上类似于 C++ 的 RAII(Resource Acquisition Is Initialization)或 Go 的 `defer`,用于确保资源(如内存、文件句柄、锁等)在离开作用域时得到正确释放。 -如果存在多个 `defer`,它们将会按照出栈方式执行。 +## 执行顺序 + +如果存在多个 `defer`,它们将会按照**后进先出(LIFO)**的顺序执行——即最后注册的 `defer` 最先执行,类似栈的出栈顺序。 <<<@/code/release/defer.zig#Defer -`defer` 分别可以执行单个语句和一个块,并且如果控制流不经过 `defer`,则不会执行。 +## 使用细节 + +- `defer` 可以执行单个语句,也可以执行一个代码块(由 `{}` 包裹)。 +- 如果控制流没有经过 `defer` 语句(例如在 `defer` 之前就 `return` 了),则该 `defer` 不会被注册,自然也不会执行。 +- `defer` 中的表达式在**作用域退出时**才会被求值,而非在 `defer` 语句出现的位置求值。 + +## 典型用法 + +`defer` 最常见的用途是配合内存分配器使用,确保分配的内存在作用域正常结束时被释放: + +```zig +const allocator = std.heap.page_allocator; +const data = try allocator.alloc(u8, 100); +defer allocator.free(data); +// 使用 data... +// 当作用域正常退出时,data 会被释放 +``` + +:::warning ⚠️ 注意 +`defer` 仅在作用域**正常退出**时执行。如果函数因返回错误而退出,已注册的 `defer` **不会执行**。如果需要在错误返回时执行清理操作,请使用 `errdefer`。 + +因此在实际的资源管理中,通常需要 `defer` 和 `errdefer` 搭配使用——`defer` 负责正常路径的清理,`errdefer` 负责错误路径的清理。 +::: + +## `errdefer` -对应 `defer` 的还有 `errdefer`,具体见这里 [`errdefer`](/basic/error_handle#errdefer)。 +对应 `defer` 的还有 `errdefer`,它仅在函数返回错误时才执行,具体见这里 [`errdefer`](/basic/error_handle#errdefer)。 diff --git a/course/basic/process_control/loop.md b/course/basic/process_control/loop.md index aa098c97..21cfc3f4 100644 --- a/course/basic/process_control/loop.md +++ b/course/basic/process_control/loop.md @@ -24,7 +24,7 @@ for 循环是另一种循环处理方式,主要用于迭代数组和切片。 <<<@/code/release/loop.zig#for_handle_array -以上代码中的 value 是一个指针,我们称之为对 数组(切片)迭代的指针捕获,注意它也是只读的,不过我们可以通过借引用指针来操作数组(切片)的值。 +以上代码中的 value 是一个指针,我们称之为对数组(切片)迭代的指针捕获。注意指针本身是不可变的(即不能让它指向别的位置),但我们可以通过解引用该指针来修改其所指向的数组(切片)元素的值。 ### 迭代数字 diff --git a/course/basic/process_control/unreachable.md b/course/basic/process_control/unreachable.md index 9bf6a22c..27bc8fa7 100644 --- a/course/basic/process_control/unreachable.md +++ b/course/basic/process_control/unreachable.md @@ -4,8 +4,19 @@ outline: deep # `unreachable` 关键字 -在 `Debug` 和 `ReleaseSafe` 模式下,`unreachable` 会触发 `panic`,并报告“不可达代码”错误。 +`unreachable` 用于标记程序中**理论上不可能到达的代码路径**。它在 Zig 的类型系统中对应 `noreturn` 类型——这是一种**底类型(Bottom Type)**,表示该表达式永远不会产生值。 -在 `ReleaseFast` 和 `ReleaseSmall` 模式下,编译器会假定永远不会执行到 `unreachable` 处,从而对代码进行优化。 +## 构建模式下的行为 + +- 在 `Debug` 和 `ReleaseSafe` 模式下,`unreachable` 会触发 `panic`,并报告"不可达代码"错误,帮助开发者发现逻辑漏洞。 +- 在 `ReleaseFast` 和 `ReleaseSmall` 模式下,编译器会**假定**永远不会执行到 `unreachable` 处,从而对代码进行优化(例如消除死代码分支)。如果程序实际运行到此处,则是未定义行为。 + +## 使用场景 + +`unreachable` 通常用于以下场景: + +1. **`switch` 语句中排除不可能的分支**:当你确定某些情况不会发生时。 +2. **类型转换中的断言**:如 `@intCast` 等操作中,编译器在某些模式下会插入 `unreachable` 来检测非法值。 +3. **配合 `noreturn` 函数**:标记在调用永不返回的函数(如 `@panic`、无限循环)之后的代码。 <<<@/code/release/unreachable.zig#unreachable diff --git a/course/basic/union.md b/course/basic/union.md index 87d44342..db2091ec 100644 --- a/course/basic/union.md +++ b/course/basic/union.md @@ -4,7 +4,7 @@ outline: deep # 联合类型 -联合类型(union)是一种特殊的类型,它在内存中划分出一块空间,用于存储多种不同类型的值,但在任何给定时间点,只能存储其中一种类型的值。 +联合类型(Union)是一种特殊的复合类型,其所有字段**共享同一块内存空间**。联合的大小等于其最大字段的大小(可能包含对齐填充)。在任何给定时间点,联合只能存储其中一种类型的值——写入一个字段会使其他字段的数据失效。 ## 基本使用 diff --git a/course/basic/zero-type.md b/course/basic/zero-type.md index 556210e8..d860b700 100644 --- a/course/basic/zero-type.md +++ b/course/basic/zero-type.md @@ -42,6 +42,4 @@ var map = std.AutoHashMap(i32, void).init(std.testing.allocator); ## 联合类型 -## 联合类型 - 仅包含一种可能类型(且该类型是零大小类型)的 [联合类型](../basic/union.md) 也是零大小类型。 diff --git a/course/engineering/build-system.md b/course/engineering/build-system.md index f17c6689..f308297b 100644 --- a/course/engineering/build-system.md +++ b/course/engineering/build-system.md @@ -25,7 +25,7 @@ Zig 使用 `build.zig` 文件来描述一个项目的构建步骤。 `build` 是构建的入口函数,而不是常见的 `main`,真正的 `main` 函数定义在 [`build_runner.zig`](https://github.com/ziglang/zig/blob/master/lib/compiler/build_runner.zig#L15) 中,这是由于 Zig 的构建分为两个阶段: -1. 生成由 [`std.Build.Step`](https://ziglang.org/documentation/master/std/#std.Build.Step) 构成有向无环图(DAG) +1. 生成由 [`std.Build.Step`](https://ziglang.org/documentation/master/std/#std.Build.Step) 构成的有向无环图(DAG,即 Directed Acyclic Graph——一种不包含环路的有向图结构,用于表达步骤之间的依赖关系) 2. 执行真正的构建逻辑 > [!TIP] diff --git a/course/index.md b/course/index.md index 852cdbb1..1de8d528 100644 --- a/course/index.md +++ b/course/index.md @@ -24,7 +24,7 @@ showVersion: false ## 为何使用 Zig -从本质上看,Zig 是一门 `low level` 的高级语言,它和 C 很像,但解决了 C 的一些历史遗留问题,并提供了完善的工具链,而且它可选支持 `libc`。 +从本质上看,Zig 是一门系统编程语言(System Programming Language),它具备底层(low-level)的内存控制能力,同时提供了比 C 更好的高级表达特性。它和 C 很像,但解决了 C 的一些历史遗留问题,并提供了完善的工具链,而且它可选支持 `libc`。 一切都是强制显式声明式,这使得代码阅读很轻松! @@ -39,14 +39,14 @@ showVersion: false - 对交叉编译的一流支持 - 标准库集成 libc,但不依赖它 - 可选类型代替 `null`,详见 [计算机科学中最严重的错误](https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/) -- 编译期(可以简单看作在编译时执行的函数)执行实现泛型和反射 +- 编译期求值(Compile-time Evaluation):在编译阶段对表达式进行求值,以此实现泛型和反射 - 无需 FFI/bindings 的 C 库集成 - 非常强大的构建系统 有几点是非常值得单独拿出来说明一下的: _Zig 会要求你显式传递和管理你的内存,并且编译时就会跟踪你的内存是否发生泄漏, -高明的可选类型(这和 rust 的 option 有异曲同工之妙),强大的编译期运行,你可实现很多花哨的操作,而构建系统则被很多 C 项目拿去作为工具链使用_ +高明的可选类型(这和 Rust 的 `Option` 有异曲同工之妙),强大的编译期运行,你可实现很多花哨的操作,而构建系统则被很多 C 项目拿去作为工具链使用_ ## 了解更多? diff --git a/course/prologue.md b/course/prologue.md index 323695fe..4a7ebd0c 100644 --- a/course/prologue.md +++ b/course/prologue.md @@ -13,7 +13,7 @@ Zig 这门语言并不适合计算机初学者,如果你已经对计算机有 ::: -有过多营销号说 zig 已经是 C 的替代品了,这是完全不正确的。将来或许会是,但现在肯定不是,目前 zig 只是一个有那么一丁点热度的高级语言。 +需要注意的是,当前 Zig 尚未成为 C 的替代品。将来或许会是,但就目前而言,Zig 仍然是一门处于快速发展中的系统编程语言,其生态和社区规模还在不断壮大。 你可能会疑惑,为什么要学习一门如此 `low level` 的语言,C 难道不好吗? @@ -24,7 +24,7 @@ C 很好,非常好,它非常成功,以至于 C 现在已经不再是一门 历史上有不少语言都自诩是 C 的替代品,像 Odin、Ada、Modula-2、Go,他们在某种程度上都算是失败了,当然 Zig 的未来也可能是这样子,成为一个不是 C 的替代品,单独的一门语言,或许有人使用。但就现在来看,我认为 Zig 还是很有希望成为 C 的代替品的,目前的语法均是围绕着语义明确化,减少隐式的执行,更高效的错误跟踪。 -或许可能有人会跟我说 Rust 比 Zig 好,我要说的是你说的基本是对的,目前情况来看,Rust 的的确确比 Zig 好很多,更为完善的生态,更多能拿得出手的代表项目,以及相较 Zig 庞大很多的社区等等,但是在未来谁说的准呢?更何况 Rust 和 Zig 并不是一个赛道上的东西,在我看来,Rust 的目标是 C++ 的替代,因此我更愿意称之为“披着高抽象皮的 low level 语言”,Zig 的目标则是 C,而且目前 Zig 的特性也的确在这个方向发展。 +Rust 和 Zig 经常被放在一起比较。客观地说,Rust 目前拥有更完善的生态、更多成熟的项目以及更庞大的社区。但两者的设计目标有所不同:Rust 侧重于通过所有权系统提供编译期的内存安全保证,更多是作为 C++ 的现代替代方案;而 Zig 则追求极致的简洁性和可控性,目标是成为 C 的现代替代方案。两者各有优势,适用于不同的场景。 Zig 的社区需要更多的人来构建,所以我写了这个文档,帮助新人来更好的理解和学习 Zig!