Ziglings 笔记 43: 结构体指针与自动解引用

告别箭头操作符

在 Ziglings 的第 43 个练习中,我们开始将指针用于更复杂的场景:结构体引用。

如果你写过 C++,你一定很熟悉 .-> 的区别:对象用点,指针用箭头。初学者经常搞混。 Zig 在这里做了一个非常棒的设计决定:统统用点 (.)

挑战:寻找导师

我们需要完善一个 RPG 角色系统。

  1. 角色可以有导师(Mentor),这通过一个指向另一个 Character 的指针来实现。
  2. 我们需要编写一个函数 printCharacter,它接收角色的指针而不是副本。

解决方案

这是修复后的 main 函数:

const std = @import("std");

// ... 结构体定义省略 ...

pub fn main() void {
    // 导师:Mighty Krodor
    // 注意:这里没有初始化 .health,因为它有默认值 100
    var mighty_krodor = Character{
        .class = Class.wizard,
        .gold = 10000,
        .experience = 2340,
    };

    // 学徒:Glorp
    var glorp = Character{ 
        .class = Class.wizard,
        .gold = 10,
        .experience = 20,
        // 关键点:取地址 (&)
        // 我们把 Krodor 的地址赋给了 mentor 字段,建立了链接
        .mentor = &mighty_krodor, 
    };

    // 修复点:传递引用
    // printCharacter 期望的是 *Character (指针),所以我们要传 &glorp
    printCharacter(&glorp);
}

核心知识点总结

1. 自动解引用 (Auto-dereference)

printCharacter 函数中,参数 c 是一个指针 *Character。 但在访问字段时,我们直接写 c.gold。 Zig 编译器非常聪明,它看到 c 是指针而 gold 是字段,就会自动帮我们解引用。这意味着在 Zig 中,你永远不需要写 c->gold

2. 结构体的默认值

const Character = struct {
    // ...
    health: u8 = 100, 
};

这个功能极大地简化了初始化代码。对于那些不常变动的配置项,设置默认值是最佳实践。

3. 枚举的简写

switch (c.class) 中,我们使用了 .wizard 而不是 Class.wizard。 只要上下文类型明确,Zig 允许我们要么省略枚举类型名,直接用点号加成员名。这让代码读起来更像一种领域特定语言 (DSL)。


后续预告:我们一直在用指针。但 C 语言老手可能会问:Zig 有没有更安全的“引用”?下一篇,我们将进入 Quiz 5,在进入 Optionals 章节前做最后一次复习。