编程语言研究书库

组织数据 — 数组、切片与结构体

组织数据 — 数组、切片与结构体

这一章给 Zig 的数据组织画一张三级台阶: 数组(编译期就知道多长, 长度是类型的一部分)、切片(运行期才定的窗口,自带长度、自带边界检查)、 结构体(自定义形状:方法、常量、默认值、位级布局)。台阶的每一级, 我们都用同一组「六颗法力石」走上去——它是本章主线例子。

1. 这一章讲什么

第二部分(第 8–11 章)进入系统级编程的心脏:数据、内存、工具。 这一章管「数据怎么摆」;第 9 章管「数据住哪(内存)」;第 10 章管「标准库给你什么」; 第 11 章管「构建系统」。位置想清楚,这一章的三个主角就不乱了: 数组和切片是「怎么摆一堆同类的东西」,结构体是「怎么把异类的东西捆成一件」。

2. 顶层全景:三级台阶

  第一级  数组 [N]T      编译期定长;长度写进类型;[5]u8 和 [6]u8 是两种类型
  第二级  切片 []T       运行期窗口 = 起始地址 + 长度;越界即 panic;字符串 = []const u8
  第三级  结构体 struct   自定义形状:字段、方法、常量、默认值、packed 位布局

  图说:从左到右,灵活性递增、编译期保证递减。
       选哪一级,取决于「长度在编译期知不知道」和「要不要异类字段」。

主线例子(原书原创,照单接住):六颗法力石 stone_powers = [42, 17, 93, 58, 11, 99](u16)。 数组段在它们身上演示定长与遍历;切片段演示运行期窗口、过滤、子串; 结构体段换一组例子(Spell / Creature / Potion),因为石头们没有异类字段可捆。

3. 第一级:数组

3.1 声明与初始化

  const message = [_]u8{ 'h', 'e', 'l', 'l', 'o' };   // [_] 让 Zig 自己数长度
  const alt_message: [5]u8 = .{ 'h', 'e', 'l', 'l', 'o' };  // 显式写长度

[_] 是「请你数清楚」的简写;[5]u8 是显式长度。长度是类型的一部分: [5]u8 和 [6]u8 是两种不同类型,不能互相赋值1。 两种写法等价,可以用编译期断言当场验证(comptime { assert(mem.eql(u8, &message, &alt_message)); })——assert 在编译期失败,程序根本编不出来2。

取长度用 .len(message.len == 5);遍历用第 4 章的 for3。

3.2 修改数组:指针迭代

数组不是不可变的(const 才锁死)。修改的标准姿势:

  var some_integers: [100]i32 = undefined;   // 100 个 i32,先不初始化
  for (&some_integers, 0..) |*item, i| {      // &数组 = 对整个数组的指针
      item.* = @intCast(i);                    // *item 解引用后赋值
  }
  // some_integers[10] == 10,some_integers[99] == 99

两个点:for (&array, 0..) |*item, i| 同时拿元素的可变指针和下标; undefined 在这里是合理用法——「我自己保证用之前填满」(第 3 章的免责书, 这里是诚实使用:紧接着的循环确实填满了)4。

3.3 编译期数组运算:++ 与 **

Zig 允许在编译期拼接和重复数组:

  const part_one = [_]i32{ 1, 2, 3, 4 };
  const part_two = [_]i32{ 5, 6, 7, 8 };
  const all_of_it = part_one ++ part_two;      // 编译期拼接 → [1,2,3,4,5,6,7,8]

  const hello_world = "hello" ++ " " ++ "world";  // 字符串也是数组,照拼
  const pattern = "ab" ** 3;                      // 重复 → "ababab"
  const all_zero = [_]u16{0} ** 10;               // 零初始化 → 10 个 0

原书的重要提醒:++ 是编译期运算,别想在运行期动态拼数组—— 运行期拼接是第 10 章 ArrayList 的活5。想清这条边界,你就不会在运行期代码里 对数组做它做不到的事。

3.4 多维数组与哨兵数组

多维数组就是数组的数组:[4][4]f32{...},用 mat[row][column] 两级下标6。

哨兵数组(sentinel-terminated array):[_:0]u8{ 1, 2, 3, 4 } 的意思是 「4 个数据 + 末尾一个哨兵值 0」,实际占 5 个内存槽,但 .len 报 4——哨兵不算数据7:

  const array = [_:0]u8{ 1, 2, 3, 4 };
  // 内存布局:[1, 2, 3, 4, 0]
  //                    ^哨兵
  array.len    == 4        // 数据长度
  array[4]     == 0        // 哨兵在槽位 4

什么时候用?和 C API 打交道——老式接口靠「特殊值结尾」而不是「显式长度」传字符串, 哨兵类型让 Zig 的类型系统替你记住这个约定8。原书的提醒:哨兵不会替你治好 off-by-one,边界自己管。

4. 第二级:切片——运行期的窗口

4.1 从主线例子出发:六颗法力石

  const stone_powers = [_]u16{ 42, 17, 93, 58, 11, 99 };   // 数组:定长 6

数组的「皇家固执」:声明之后不能改长度,长度还是类型的一部分9。 运行期来了新需求——「玩家进地牢,只能看到一部分石头」——数组自己做不到, 切片登场。

4.2 主线例子走查:运行期切一刀

  var known_at_runtime_zero: usize = 0;
  _ = &known_at_runtime_zero;    // 编译器技巧:强迫它被视为「运行期才知道」

  const middle_stones = stone_powers[known_at_runtime_zero..stone_powers.len];
  // middle_stones 的类型是 []u16 —— 切片,不是数组

_ = &变量 这一行值得停一秒:它取变量的地址再丢弃,作用是告诉编译器 「假装你不知道这个值」10。没有它,编译器会看出 0 是常量, 把整个切片优化回 [6]u16 数组类型——例子就演示不到真正的运行期切片了。 (这是教学技巧,实际代码不需要这一手;但背后的现象是真的: 切片的下标是否编译期已知,影响结果的类型。)

切片的本质(第 3 章立过,这里兑现):起始地址 + 长度。它不复制数据, 只是换了个窗口看原数组;窗口多大,运行期说了算11。

4.3 边界检查:切片替你站岗

  const subset = stone_powers[1..3];   // 元素 17 和 93,长度 2
  // subset[99] = 100;                 // ← 运行期 panic,不是静默出错

对切片越界访问,安全模式下运行期 panic,而不是 C 那样悄无声息地踩坏内存12。 这一句话是全章安全叙事的核心:切片把「长度」从约定变成数据结构的一部分, 于是「检查」从自觉变成必然。第 1 章的越界安全网,机制就在这里。

对照一记(原书注释里的细节):数组下标在安全模式同样会 panic; 书里的注释说「arrays do no runtime safety checks (by default)」指的是 「编译期已知的下标越界会在编译期被拒」,不是运行时裸奔13。 两种容器在 Debug/ReleaseSafe 下都受保护,区别只在「长度是不是类型的一部分」。

4.4 主线例子续:运行期过滤

需求:挑出力量 > 50 的石头,数量运行期才知道。做法两步:

  var count: usize = 0;
  for (stone_powers) |power| {
      if (power > 50) count += 1;     // ① 先数:93、58、99 → count = 3
  }
  const start_index: usize = 2;
  var length: usize = 3;
  _ = &length;

  const good_stones = stone_powers[start_index..][0..length];  // ② 再切
  // good_stones = [93, 58, 11],类型 []u16

[start..][0..length] 是切两次:先定起点,再定长度, 两次都带运行期成分,结果稳稳是切片类型14。走查数值: 起点 2 → 从 93 开始;长度 3 → 93、58、11。 (注意:这个例子演示的是「切片语法」,不是「真正的过滤」—— 真正的过滤要新建数组装结果,那是第 10 章 ArrayList 的活。书里用「假设我们知道 合格石头连在一起」简化了问题,我们如实标注。)

4.5 字符串切片与子串

  const spell_name: []const u8 = "Caldara's Blaze";
  const substring = spell_name[10..];     // 下标 10 是 'B'
  // substring == "Blaze"

数一遍:C(0)a(1)l(2)d(3)a(4)r(5)a(6)'(7)s(8) (9)B(10)—— 所以 [10..] 正好切出 "Blaze"15。字符串没有任何特殊类型, 就是 []const u8,于是切片语法原样适用。越界同样 panic。

4.6 哨兵切片:对接老 API

  const legacy_name: [:0]const u8 = "Elixir";
  // len == 6,legacy_name[6] == 0(哨兵)

[:0]const u8 = 「以 0 结尾的字节切片」16。它带来一个强检查: 如果你从一个末尾没有 0 的数组硬造哨兵切片,运行期 panic—— 类型说了谎,当场被抓17。和第 6 章的精神一致:可以声明约定,但违约必须炸在明处。

5. 第三级:结构体(struct)

5.1 基本形态

  const Spell = struct {
      name: []const u8,
      mana_cost: u32,
      potency: f32,
  };

  const fireball = Spell{
      .name = "Fireball",
      .mana_cost = 25,
      .potency = 0.75,
  };

结构体把异类的字段捆成一件:字节切片、整数、浮点可以同住18。 初始化用 .字段 = 值 的「结构体字面量」,每个字段都必须给(第 1 章那张安全网)。 一个反直觉的点:字段在内存里的顺序编译器不保证——它会重排来优化布局, 你不该依赖顺序,也(一般)不用关心19。

5.2 方法

  const Creature = struct {
      health: f32,
      mana: u32,

      pub fn attack(self: *Creature, spell: Spell) bool {
          if (self.mana < spell.mana_cost) return false;
          self.mana -= spell.mana_cost;
          return true;
      }
  };

方法不是魔法,只是住在结构体里的函数,第一个参数是 self 指针20。 调用用点语法:goblin.attack(frost_bolt)。走查原书测试:

  goblin: health 100,mana 50;frost_bolt 耗 20
    第 1 次 attack → true,mana 50-20 = 30
    第 2 次 attack → true,mana 30-20 = 10
    第 3 次 attack → mana 10 < 20 → false,mana 保持 10

原书补了一句立场:没有 OOP 继承——Zig 保持直接和诚实21。 想要「复用行为」,组合(结构体套结构体)和函数是全部工具。

5.3 常量与空结构体

结构体可以只装常量,当一个零成本的命名空间:

  const Alchemy = struct {
      pub const BASE_MULTIPLIER = 2.5;
  };
  const EmptySpace = struct {
      pub const PI = 3.14159;    // 一个字段都没有,完全合法
  };

  @sizeOf(EmptySpace) == 0     // 零运行时成本

@sizeOf 是内建函数:问一个类型占多少字节。空结构体占 0 字节—— 它纯粹是编译期的组织工具22。这个「零大小」类型会在第 9 章的 「零大小类型」一节正式登场。

5.4 @fieldParentPtr:从字段倒推父结构体

  const Creature = struct { health: f32, mana: u32 };

  fn boostMana(mana_ptr: *u32, amount: u32) void {
      const creature_ptr: *Creature = @fieldParentPtr("mana", mana_ptr);
      creature_ptr.mana += amount;
  }

  var elf = Creature{ .health = 150.0, .mana = 10 };
  boostMana(&elf.mana, 40);   // elf.mana: 10 → 50

只拿到一个字段的指针,能反推出整个父结构体的指针23。 原理:字段在结构体里的偏移是编译期已知的,父地址 = 字段地址 − 偏移。 什么时候真用?底层代码(容器结构、嵌入式列表节点)——第 9、13 章会再见到这个手法。 对应用层代码,它是「知道有这招」级别的知识。

5.5 默认字段值:方便,但别破坏不变量

  const Potion = struct {
      strength: u16 = 10,        // 有默认值
      flavor_rating: u8,         // 没有,初始化必须给
  };
  const brew = Potion{ .flavor_rating = 8 };   // strength 自动是 10

省略有默认值的字段是合法的24。原书的告诫框值得照收: 如果默认值可能破坏不变量(比如让 max < min),别用默认值, 改成提供一个「出厂即合法」的构造函数25。默认值是语法糖,不是语义保证。

5.6 packed struct:位级布局控制

普通结构体的内存布局听编译器的;packed struct 把每一个比特都交给你:

  const TinyData = packed struct {
      high: u4,    // 恰好 4 位
      low:  u4,    // 恰好 4 位
  };
  // @sizeOf(TinyData) == 1:两个半字节精确装进一个字节

对照(原书并排给的)26:

普通 structpacked struct
flag: bool + value: u16可能因对齐补洞,合计或占 4 字节flag: u1 + value: u15 恰好 16 位,合计 2 字节
布局由谁决定编译器择优你逐位指定
什么时候用默认二进制协议、硬件寄存器、老 C API

代价原书说得很直白:大权在握,责任自负,走进对齐和打包的雷区, Zig 不拦着你造自己的脚枪27。(关于 packed 的字段取地址限制等细则, 本章没展开——那是第 9 章指针的活。)

5.7 匿名结构体与元组

不想为一个临时组合起名字?两种匿名形态:

  // 匿名结构体:有字段名,类型由函数签名或 anytype 推断
  try check(.{ .max_health = 200, .respawn_enabled = true });
  fn check(settings: anytype) !void { ... }   // anytype = 任何类型(编译期展开)

  // 元组:连字段名都省了,用位置当名字
  const hero_stats = .{100, 50.5, "strong"};
  hero_stats.len       // 3
  hero_stats.@"0"      // 100(数字名字要用 @"" 语法引)

anytype 参数 + 匿名结构体是 Zig 的「轻量泛型」日常形态—— 编译期为每个调用点生成一份具体代码(第 12 章 comptime 的正餐前的点心)28。

6. 作者的判断与证据

可核的: 全部类型规则([N]T 长度入类型、切片边界检查 panic、哨兵语义、 packed 的 @sizeOf、空结构体零大小)都有原书 test 块现场验证,数字照抄可跑71222。 _ = &x 强迫运行期语义的技巧有专门解释10。

立场: 「没有继承,保持直接诚实」21、「默认值别破坏不变量」25、 「packed 大权在握责任自负」27——都是 Zig 社区的主流价值。 「数组不是运行期的动态朋友」5这个判断是对的,但它把运行期拼接直接推给第 10 章, 中间没给读者任何过渡(运行期拼数组到底长什么样,本章看不到)。

7. 边界与局限

缺什么说明
数组越界到底什么时候查,讲得含糊§4.3 那条注释(「arrays do no runtime safety checks by default」)和第 4 章的叙事有张力:编译期已知越界编译报错、运行期下标安全模式 panic——两个机制本章没有系统区分
「过滤」例子名不副实§4.4 的「runtime filtering」实际切的是「假设已知的连续段」,真正的过滤(新数组装结果)没有出现
结构体内存布局只有两句字段重排、对齐、padding 的原理没展开(第 9 章也不系统讲),「@sizeOf 普通结构体」读者只能猜
packed struct 的指针限制没提packed 字段不能单独取地址等约束,是实际写 packed 代码的第一个坑
嵌套结构体/自引用结构体没出现链表节点这种系统级必备形态,本章没有(第 9 章指针会接近它)
15 道章末题覆盖面好但无解析答案只有字母29,做错回不到具体段落

7.5 加深一层:三个值得摊开的问题

这一节是我们补的台阶(标注来源),原书点到即止。

7.5.1 「长度是类型的一部分」到底改变了什么

别的语言的数组(如 C)是「一块内存 + 你自己记长度」;Zig 的 [6]u16 是 类型本身携带长度。这个设计带来一串连锁反应,值得一次看清:

连锁反应例子
长度不同的数组不能互换[5]u8 传给期望 [6]u8 的函数,编译错误
越界能被编译期抓住一部分arr[10] 而 arr 长 6,编译器直接拒
切片要单独一个类型[]u16 与 [6]u16 是两个世界,桥靠 &arr 和 arr[a..b]
泛型天然带长度fn sum(comptime n: usize, arr: [n]i32) 可以按长度特化(第 12 章)

代价是灵活性:长度变了类型就变,接口想要「任意长」必须上切片。 数组管「形状已知的快」,切片管「形状未知的活」——本章三级台阶的实质是这个分工。

7.5.2 切片为什么比指针安全(以及它不防什么)

切片 = 指针 + 长度,于是边界检查成为可能:访问前先比下标和长度, 越界 panic。它不防的是两类错(补充,来自通用知识): 悬垂(底层数组被释放后切片还在——第 9 章的 lifetime 话题)和 迭代中改长度(遍历切片时把它背后的数组改了)。边界检查护住「越界」, 护不住「还活着吗」;后者是 Zig 划给你的责任区,正是第 9 章的主线。

7.5.3 _ = &x 技巧的通用意义

§4.2 那个「取地址再丢弃」的把戏,本质是阻止编译期的常量传播。 它在真实代码里有个正经用途:写基准测试(benchmark)时,防止编译器把 被测计算整个优化掉。本章把它用作教学,顺手记住它的原理—— 你以后在 Zig 的基准测试代码里会经常见到这个写法。

8. 可带走的

主线例子,一行走完: 六颗法力石 [42,17,93,58,11,99](定长数组) → [runtime_zero..6] 切出 []u16(运行期窗口)→ 数出 3 颗 >50 → [2..][0..3] 切出 [93,58,11];字符串 "Caldara's Blaze"[10..] = "Blaze"。

  1. 数组 [N]T:长度是类型的一部分,编译期定死;[_] 让 Zig 替你数;
  2. .len 取长度;&array 在 for 里拿到可变指针;undefined 数组 + 马上填满,是合法技巧;
  3. ++(拼接)和 **(重复)是编译期运算——运行期拼数组找 ArrayList(第 10 章);
  4. 切片 = 起始地址 + 长度,不复制数据;越界运行期 panic(安全模式); 下标是否编译期已知,影响切片结果的类型;
  5. 字符串就是 []const u8,子串 = 切片,越界一样 panic;
  6. 哨兵数组/切片 [_:0]u8 / [:0]const u8:末尾自带哨兵,对接 C 约定; 从没有哨兵的数据硬造哨兵切片会 panic;
  7. 结构体:字段必须全初始化;字段内存顺序不保证;方法 = 第一个参数是 self 的函数; 没有继承;
  8. 空结构体占 0 字节,可当零成本常量命名空间;
  9. 默认字段值是糖不是保证,可能破坏不变量时改用构造函数;
  10. packed struct 逐位控制布局(u4+u4=1 字节),代价是雷区自负; 匿名结构体 + anytype 是轻量泛型,元组用 @"0" 这种数字字段名。

9. 原文地图

主题原书章原文位置
[_] 与 [N] 声明Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:37(搜「const message = [_]u8」) · text/15-ch08-chapter-8-organizing-data.txt:42(搜「const alt_message: [5]u8」)
编译期 assert 验证等价Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:44(搜「comptime」)
.len 与遍历Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:62(搜「get its length with .len」) · text/15-ch08-chapter-8-organizing-data.txt:77(搜「for (message)」)
修改数组(指针迭代)Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:89(搜「some_integers: [100]i32」)
编译期 ++ 拼接Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:104(搜「part_one ++ part_two」) · text/15-ch08-chapter-8-organizing-data.txt:116(搜「hello ++」)
** 重复与零初始化Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:124(搜「"ab" ** 3」) · text/15-ch08-chapter-8-organizing-data.txt:133(搜「{0} ** 10」)
多维数组Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:145(搜「mat4x4」)
哨兵数组Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:159(搜「N + 1 items」) · text/15-ch08-chapter-8-organizing-data.txt:172(搜「[4:0]u8」)
六颗法力石(主线输入)Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:217(搜「42, 17, 93, 58, 11, 99」)
数组下标检查的注释Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:224(搜「do no runtime safety checks」)
_ = &x 强迫运行期Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:278(搜「compiler trick」)
切片边界检查Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:298(搜「protect you from yourself」) · text/15-ch08-chapter-8-organizing-data.txt:316(搜「subset[99]」)
运行期过滤(切两次)Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:336(搜「slices enabling runtime filtering」) · text/15-ch08-chapter-8-organizing-data.txt:348(搜「[start_index..][0..length]」)
字符串子串Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:398(搜「Caldara's Blaze」) · text/15-ch08-chapter-8-organizing-data.txt:405(搜「spell_name[start_of_substring..]」)
哨兵切片Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:432(搜「[:0]const u8 = "Elixir"」) · text/15-ch08-chapter-8-organizing-data.txt:440(搜「bad_slice」)
数组与切片小结Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:455(搜「runtime flexibility」)
Spell 结构体Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:498(搜「const Spell = struct」)
字段顺序不保证Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:516(搜「order of fields」)
方法与 goblin 走查Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:540(搜「pub fn attack」) · text/15-ch08-chapter-8-organizing-data.txt:551(搜「goblin = Creature」)
无继承Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:567(搜「inheritance」)
空结构体零大小Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:581(搜「EmptySpace」) · text/15-ch08-chapter-8-organizing-data.txt:586(搜「@sizeOf(EmptySpace)」)
@fieldParentPtrChapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:628(搜「@fieldParentPtr("mana"」)
默认字段值Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:650(搜「const Potion = struct」) · text/15-ch08-chapter-8-organizing-data.txt:671(搜「risk breaking invariants」)
packed structChapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:685(搜「packed struct」) · text/15-ch08-chapter-8-organizing-data.txt:694(搜「@sizeOf(TinyData)」)
packed vs normalChapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:707(搜「Normal struct - compiler optimizes」)
匿名结构体与 anytypeChapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:734(搜「anonymous structs」) · text/15-ch08-chapter-8-organizing-data.txt:742(搜「anytype」)
元组Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:756(搜「const hero_stats」) · text/15-ch08-chapter-8-organizing-data.txt:762(搜「.@"0" == 100」)
章末 15 题Chapter 8: Organizing Datatext/15-ch08-chapter-8-organizing-data.txt:801(搜「Let’s make this knowledge stick」) · text/15-ch08-chapter-8-organizing-data.txt:913(搜「correct answers」)
第二部分地图Part 2: Data, Memory, and Toolstext/14-ch02-part-2-data-memory-and-tools.txt:3(搜「heart of systems programming」)

Footnotes

  1. 出处:「Chapter 8: Organizing Data」第 34–52 段(text/15-ch08-chapter-8-organizing-data.txt:37,搜「const message = [_]u8」;text/15-ch08-chapter-8-organizing-data.txt:42,搜「const alt_message: [5]u8」)。 ↩

  2. 出处:同章第 43–52 段(text/15-ch08-chapter-8-organizing-data.txt:44,搜「comptime」;text/15-ch08-chapter-8-organizing-data.txt:51,搜「won’t build」)。 ↩

  3. 出处:同章第 61–80 段(text/15-ch08-chapter-8-organizing-data.txt:62,搜「get its length with .len」;text/15-ch08-chapter-8-organizing-data.txt:77,搜「for (message)」)。 ↩

  4. 出处:同章第 85–98 段(text/15-ch08-chapter-8-organizing-data.txt:89,搜「some_integers: [100]i32」;text/15-ch08-chapter-8-organizing-data.txt:97,搜「don’t bother initializing」)。 ↩

  5. 出处:同章第 100–129 段(text/15-ch08-chapter-8-organizing-data.txt:104,搜「part_one ++ part_two」;text/15-ch08-chapter-8-organizing-data.txt:110,搜「Don’t even think about trying to do this dynamically」)。 ↩ ↩2

  6. 出处:同章第 142–154 段(text/15-ch08-chapter-8-organizing-data.txt:145,搜「mat4x4」;text/15-ch08-chapter-8-organizing-data.txt:152,搜「mat4x4[row][column]」)。 ↩

  7. 出处:同章第 156–180 段(text/15-ch08-chapter-8-organizing-data.txt:159,搜「N + 1 items」;text/15-ch08-chapter-8-organizing-data.txt:165,搜「STOP sign」;text/15-ch08-chapter-8-organizing-data.txt:172,搜「[4:0]u8」)。 ↩ ↩2

  8. 出处:同章第 177–180 段(text/15-ch08-chapter-8-organizing-data.txt:178,搜「termination-by-a-special-value」)。 ↩

  9. 出处:同章第 207–230 段(text/15-ch08-chapter-8-organizing-data.txt:217,搜「42, 17, 93, 58, 11, 99」;text/15-ch08-chapter-8-organizing-data.txt:219,搜「length is part of its type」)。 ↩

  10. 出处:同章第 252–294 段(text/15-ch08-chapter-8-organizing-data.txt:278,搜「compiler trick」;text/15-ch08-chapter-8-organizing-data.txt:282,搜「true runtime slice」)。 ↩ ↩2

  11. 出处:同章第 247–294 段(text/15-ch08-chapter-8-organizing-data.txt:288,搜「non-compiletime known slice」)。 ↩

  12. 出处:同章第 296–322 段(text/15-ch08-chapter-8-organizing-data.txt:298,搜「protect you from yourself」;text/15-ch08-chapter-8-organizing-data.txt:316,搜「subset[99]」)。 ↩ ↩2

  13. 出处:同章第 224–227 段(text/15-ch08-chapter-8-organizing-data.txt:224,搜「do no runtime safety checks」)。原注释全文:「arrays do no runtime safety checks (by default), but if you're testing in safe mode, Zig will still panic on out-of-bounds」——上下文指编译期已知下标的越界在编译期被拒。 ↩

  14. 出处:同章第 326–373 段(text/15-ch08-chapter-8-organizing-data.txt:336,搜「slices enabling runtime filtering」;text/15-ch08-chapter-8-organizing-data.txt:348,搜「[start_index..][0..length]」)。 ↩

  15. 出处:同章第 393–423 段(text/15-ch08-chapter-8-organizing-data.txt:398,搜「Caldara's Blaze」;text/15-ch08-chapter-8-organizing-data.txt:405,搜「spell_name[start_of_substring..]」)。 ↩

  16. 出处:同章第 425–452 段(text/15-ch08-chapter-8-organizing-data.txt:432,搜「[:0]const u8 = "Elixir"」)。 ↩

  17. 出处:同章第 437–452 段(text/15-ch08-chapter-8-organizing-data.txt:440,搜「bad_slice」;text/15-ch08-chapter-8-organizing-data.txt:450,搜「mismatch at runtime and panic」)。 ↩

  18. 出处:同章第 473–517 段(text/15-ch08-chapter-8-organizing-data.txt:498,搜「const Spell = struct」)。 ↩

  19. 出处:同章第 515–517 段(text/15-ch08-chapter-8-organizing-data.txt:516,搜「order of fields」)。 ↩

  20. 出处:同章第 519–564 段(text/15-ch08-chapter-8-organizing-data.txt:540,搜「pub fn attack」;text/15-ch08-chapter-8-organizing-data.txt:522,搜「aren’t magic」)。 ↩

  21. 出处:同章第 566–568 段(text/15-ch08-chapter-8-organizing-data.txt:567,搜「inheritance」)。 ↩ ↩2

  22. 出处:同章第 570–607 段(text/15-ch08-chapter-8-organizing-data.txt:581,搜「EmptySpace」;text/15-ch08-chapter-8-organizing-data.txt:586,搜「@sizeOf(EmptySpace)」)。 ↩ ↩2

  23. 出处:同章第 609–641 段(text/15-ch08-chapter-8-organizing-data.txt:610,搜「@fieldParentPtr」;text/15-ch08-chapter-8-organizing-data.txt:628,搜「@fieldParentPtr("mana"」)。 ↩

  24. 出处:同章第 643–667 段(text/15-ch08-chapter-8-organizing-data.txt:650,搜「const Potion = struct」;text/15-ch08-chapter-8-organizing-data.txt:656,搜「.flavor_rating = 8」)。 ↩

  25. 出处:同章第 669–672 段(text/15-ch08-chapter-8-organizing-data.txt:671,搜「risk breaking invariants」)。 ↩ ↩2

  26. 出处:同章第 674–723 段(text/15-ch08-chapter-8-organizing-data.txt:685,搜「packed struct」;text/15-ch08-chapter-8-organizing-data.txt:694,搜「@sizeOf(TinyData)」;text/15-ch08-chapter-8-organizing-data.txt:707,搜「Normal struct - compiler optimizes」)。 ↩

  27. 出处:同章第 721–723 段(text/15-ch08-chapter-8-organizing-data.txt:722,搜「making your own foot」)。 ↩ ↩2

  28. 出处:同章第 725–768 段(text/15-ch08-chapter-8-organizing-data.txt:734,搜「anonymous structs」;text/15-ch08-chapter-8-organizing-data.txt:756,搜「const hero_stats」)。 ↩

  29. 出处:同章第 801–929 段(text/15-ch08-chapter-8-organizing-data.txt:801,搜「Let’s make this knowledge stick」;text/15-ch08-chapter-8-organizing-data.txt:913,搜「correct answers」)。 ↩

On this page