组织数据 — 数组、切片与结构体¶
这一章给 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 从主线例子出发:六颗法力石¶
数组的「皇家固执」:声明之后不能改长度,长度还是类型的一部分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 边界检查:切片替你站岗¶
对切片越界访问,安全模式下运行期 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¶
[: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:
| 普通 struct | packed 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"。
- 数组
[N]T:长度是类型的一部分,编译期定死;[_]让 Zig 替你数; .len取长度;&array在 for 里拿到可变指针;undefined数组 + 马上填满,是合法技巧;++(拼接)和**(重复)是编译期运算——运行期拼数组找 ArrayList(第 10 章);- 切片 = 起始地址 + 长度,不复制数据;越界运行期 panic(安全模式); 下标是否编译期已知,影响切片结果的类型;
- 字符串就是
[]const u8,子串 = 切片,越界一样 panic; - 哨兵数组/切片
[_:0]u8/[:0]const u8:末尾自带哨兵,对接 C 约定; 从没有哨兵的数据硬造哨兵切片会 panic; - 结构体:字段必须全初始化;字段内存顺序不保证;方法 = 第一个参数是 self 的函数; 没有继承;
- 空结构体占 0 字节,可当零成本常量命名空间;
- 默认字段值是糖不是保证,可能破坏不变量时改用构造函数;
- packed struct 逐位控制布局(u4+u4=1 字节),代价是雷区自负;
匿名结构体 + anytype 是轻量泛型,元组用
@"0"这种数字字段名。
9. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| [_] 与 [N] 声明 | Chapter 8: Organizing Data | 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」) |
| 编译期 assert 验证等价 | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:44(搜「comptime」) |
| .len 与遍历 | Chapter 8: Organizing Data | 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)」) |
| 修改数组(指针迭代) | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:89(搜「some_integers: [100]i32」) |
| 编译期 ++ 拼接 | Chapter 8: Organizing Data | text/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 Data | text/15-ch08-chapter-8-organizing-data.txt:124(搜「"ab" ** 3」) · text/15-ch08-chapter-8-organizing-data.txt:133(搜「{0} ** 10」) |
| 多维数组 | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:145(搜「mat4x4」) |
| 哨兵数组 | Chapter 8: Organizing Data | text/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 Data | text/15-ch08-chapter-8-organizing-data.txt:217(搜「42, 17, 93, 58, 11, 99」) |
| 数组下标检查的注释 | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:224(搜「do no runtime safety checks」) |
| _ = &x 强迫运行期 | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:278(搜「compiler trick」) |
| 切片边界检查 | Chapter 8: Organizing Data | text/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 Data | 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]」) |
| 字符串子串 | Chapter 8: Organizing Data | 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..]」) |
| 哨兵切片 | Chapter 8: Organizing Data | text/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 Data | text/15-ch08-chapter-8-organizing-data.txt:455(搜「runtime flexibility」) |
| Spell 结构体 | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:498(搜「const Spell = struct」) |
| 字段顺序不保证 | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:516(搜「order of fields」) |
| 方法与 goblin 走查 | Chapter 8: Organizing Data | text/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 Data | text/15-ch08-chapter-8-organizing-data.txt:567(搜「inheritance」) |
| 空结构体零大小 | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:581(搜「EmptySpace」) · text/15-ch08-chapter-8-organizing-data.txt:586(搜「@sizeOf(EmptySpace)」) |
| @fieldParentPtr | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:628(搜「@fieldParentPtr("mana"」) |
| 默认字段值 | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:650(搜「const Potion = struct」) · text/15-ch08-chapter-8-organizing-data.txt:671(搜「risk breaking invariants」) |
| packed struct | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:685(搜「packed struct」) · text/15-ch08-chapter-8-organizing-data.txt:694(搜「@sizeOf(TinyData)」) |
| packed vs normal | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:707(搜「Normal struct - compiler optimizes」) |
| 匿名结构体与 anytype | Chapter 8: Organizing Data | text/15-ch08-chapter-8-organizing-data.txt:734(搜「anonymous structs」) · text/15-ch08-chapter-8-organizing-data.txt:742(搜「anytype」) |
| 元组 | Chapter 8: Organizing Data | text/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 Data | 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」) |
| 第二部分地图 | Part 2: Data, Memory, and Tools | text/14-ch02-part-2-data-memory-and-tools.txt:3(搜「heart of systems programming」) |
-
出处:「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」)。 ↩ -
出处:同章第 43–52 段(
text/15-ch08-chapter-8-organizing-data.txt:44,搜「comptime」;text/15-ch08-chapter-8-organizing-data.txt:51,搜「won’t build」)。 ↩ -
出处:同章第 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)」)。 ↩ -
出处:同章第 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」)。 ↩ -
出处:同章第 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」)。 ↩↩ -
出处:同章第 142–154 段(
text/15-ch08-chapter-8-organizing-data.txt:145,搜「mat4x4」;text/15-ch08-chapter-8-organizing-data.txt:152,搜「mat4x4[row][column]」)。 ↩ -
出处:同章第 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」)。 ↩↩ -
出处:同章第 177–180 段(
text/15-ch08-chapter-8-organizing-data.txt:178,搜「termination-by-a-special-value」)。 ↩ -
出处:同章第 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」)。 ↩ -
出处:同章第 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」)。 ↩↩ -
出处:同章第 247–294 段(
text/15-ch08-chapter-8-organizing-data.txt:288,搜「non-compiletime known slice」)。 ↩ -
出处:同章第 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]」)。 ↩↩ -
出处:同章第 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」——上下文指编译期已知下标的越界在编译期被拒。 ↩ -
出处:同章第 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]」)。 ↩ -
出处:同章第 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..]」)。 ↩ -
出处:同章第 425–452 段(
text/15-ch08-chapter-8-organizing-data.txt:432,搜「[:0]const u8 = "Elixir"」)。 ↩ -
出处:同章第 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」)。 ↩ -
出处:同章第 473–517 段(
text/15-ch08-chapter-8-organizing-data.txt:498,搜「const Spell = struct」)。 ↩ -
出处:同章第 515–517 段(
text/15-ch08-chapter-8-organizing-data.txt:516,搜「order of fields」)。 ↩ -
出处:同章第 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」)。 ↩ -
出处:同章第 566–568 段(
text/15-ch08-chapter-8-organizing-data.txt:567,搜「inheritance」)。 ↩↩ -
出处:同章第 570–607 段(
text/15-ch08-chapter-8-organizing-data.txt:581,搜「EmptySpace」;text/15-ch08-chapter-8-organizing-data.txt:586,搜「@sizeOf(EmptySpace)」)。 ↩↩ -
出处:同章第 609–641 段(
text/15-ch08-chapter-8-organizing-data.txt:610,搜「@fieldParentPtr」;text/15-ch08-chapter-8-organizing-data.txt:628,搜「@fieldParentPtr("mana"」)。 ↩ -
出处:同章第 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」)。 ↩ -
出处:同章第 669–672 段(
text/15-ch08-chapter-8-organizing-data.txt:671,搜「risk breaking invariants」)。 ↩↩ -
出处:同章第 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」)。 ↩ -
出处:同章第 721–723 段(
text/15-ch08-chapter-8-organizing-data.txt:722,搜「making your own foot」)。 ↩↩ -
出处:同章第 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」)。 ↩ -
出处:同章第 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」)。 ↩