Ziglings 笔记 54: 多项指针与类型大一统

指针的完全体

在 Ziglings 的第 54 个练习中,我们终于集齐了 Zig 指针家族的最后一块拼图:多项指针 (Many-item Pointer)

在此之前,我们学过单项指针 *T(只能指一个)和切片 []T(带长度)。但如果你需要像 C 语言那样操作一个“不知道长度的数组指针”怎么办?Zig 提供了 [*]T

挑战:找回丢失的长度

代码中我们有一个 zen_manyptr,它指向一段字符串内存,但它的类型是 [*]const u8。这意味着编译器只知道它指向哪里,但不知道它有多长。 为了使用 {s} 格式化打印它,我们需要把它转换回切片 []const u8

解决方案

我们需要手动指定切片的边界:

const std = @import("std");

pub fn main() void {
    // 原始数据:指向长为 21 的数组的指针
    const zen12: *const [21]u8 = "Memory is a resource.";

    // 转换为多项指针:此时长度信息丢失了!
    // [*]const u8 就像 C 语言里的 const char*
    const zen_manyptr: [*]const u8 = zen12;

    // 核心操作:指针 -> 切片
    // 因为多项指针不知道长度,我们必须显式通过 [0..N] 告诉编译器截取多少
    // 字符串 "Memory is a resource." 的长度正好是 21
    const zen12_string: []const u8 = zen_manyptr[0..21];

    std.debug.print("{s}\n", .{zen12_string});
}

核心知识点总结

1. [*]T vs []T

这是系统编程中常见的权衡:

  • 切片 ([]T)胖指针。占用 2 个字长(地址 + 长度)。安全,知道边界。
  • 多项指针 ([*]T)瘦指针。占用 1 个字长(地址)。不安全,不知道边界,但与 C 语言兼容性好。

2. 指针速查表 (The Cheatsheet)

练习中的注释总结了 Zig 所有的指针类型,非常值得收藏:

  • u8: 值。
  • *u8: 单个指针。
  • [*]u8: C 风格指针(多项)。
  • []u8: 切片(指针 + 长度)。
  • [N]u8: 数组(固定长度的值)。
  • *[N]u8: 指向数组的单指针。

3. 何时使用?

在纯 Zig 代码中,99% 的情况你应该使用切片 ([]T)。 只有在以下情况使用多项指针 [*]T

  1. 调用 C 语言接口。
  2. 实现底层的内存分配器。
  3. 进行高性能的指针算术运算。

后续预告:指针和数组我们已经学透了。接下来的练习将进入一个新的高阶话题:泛型 (Generics)。Zig 没有 <T> 这样的语法,它是如何在编译期实现泛型的呢?下一篇见分晓。