内存管理 — 栈、堆、指针与分配器
内存管理 — 栈、堆、指针与分配器
这一章是全书最硬的一章,也是「没有 GC」承诺的兑现章。 五块内容:栈和堆的分工与各自的死法;指针的完整形态(单项
*T、多项[*]T、 切片这个「胖指针」);分配器协议和四个真分配器的选型;生命周期纪律 (悬垂指针怎么来、init/deinit 约定怎么挡它);类型转换的分层 (安全的自动转、危险的显式转、分支类型的自动协商)。
1. 这一章讲什么
链条位置:第 8 章把数据摆好,这一章讲数据住在哪、怎么租、怎么还。 原书自己的定位很准:「你挺过了 Zig 最粗粝的一面」——后面第 10 章的标准库, 就是把这些手工活封装成工具的章节。
2. 顶层全景
住哪 栈:函数的工作台,自动收拾,小而快;死了叫栈溢出
堆:按需租的仓库,自己还;死了叫泄漏;两者相撞(32 位)叫灾难
指哪 *T 单项指针(不许算术)│ [*]T 多项指针(可以算术)│ []T 切片(指针+长度,胖指针)
租哪 Allocator 协议:alloc / free / resize(传参显式,无默认分配器)
选型:FixedBuffer(定长池)│ GPA(万用)│ page(OS 直供)│ arena(成批还)
活多久 不许返回栈内存的指针(悬垂);init/deinit 约定;errdefer 保错误路径
怎么转 coercion(自动、安全)│ @intCast/@bitCast(显式、有风险)│ peer type resolution
图说:五个问题——住哪、指哪、租哪、活多久、怎么转——对应五节。主线例子:原书的 Sword 结构体——init 在堆里造剑、deinit 还剑,
配上 GPA 的泄漏报告走查。一条线把「租、用、还、查」全走完。
3. 栈与堆:两种住法,两种死法
3.1 栈:自动收拾的小工作台
栈是给函数存局部变量的内存区。调用函数,压入一块新的、干净的工作区; 函数返回,整块立即自动清空——没有手工清理,没有查找,所以极快1。 限制有二:空间小(Linux 主线程默认约 8 MB, reckless 的大数组或深递归直接撑爆, 这就是栈溢出);大小必须在编译期知道2。
3.2 堆:按需租的大仓库
堆是可以运行时申请的大空间。流程:你的代码请分配器(allocator)要内存, 分配器再向操作系统(内核,管理内存的 OS 核心)成块地要;OS 答应了就有, 不答应就失败。用完必须自己 free——忘了,就是内存泄漏3。 分配和释放都比栈慢,还会有碎片化(大小各异的请求来来去去,仓库里出现用不上的碎缝)。
32 位的补充(原书边注):32 位程序的虚拟地址空间只有 4 GB,堆向上长、栈向下长, 两者相撞程序就崩;64 位的虚拟空间大得多(2⁴⁴ 量级,原书说 16 exabytes), 相撞几乎不会发生4。这个细节对「为什么嵌入式/老平台更怕堆」是个注脚。
其余分区(全局数据、代码、BSS 未初始化数据)由 OS 和加载器管,日常不用碰, 调试怪 bug 时知道存在即可5。
3.3 为什么需要指针
指针解决两个头疼:对象大小编译期不知道(运行时才知道要多少), 和就算知道也不想到处拷贝(大结构体传值太贵)6。 原书的比方:与其把仓库里的全部家当推过走廊,不如递一把钥匙7。
4. 指针全家桶
4.1 取地址 & 与解引用 .*
test "managing player health using pointers" {
const npc_health: i32 = 100;
const npc_health_ptr = &npc_health; // & = 取地址
try expect(@TypeOf(npc_health_ptr) == *const i32); // const 变量的地址 → *const T
var player_health: i32 = 150;
const player_health_ptr = &player_health; // var 变量的地址 → *T
player_health_ptr.* -= 20; // .* = 「这个地址里住的东西」
try expect(player_health == 130); // 原变量跟着变
}走查:NPC 血 100,取地址得 *const i32——const 变量的指针不可通过它修改;
玩家血 150,取地址得 *i32,通过 ptr.* -= 20 直接改原值到 1308。
Zig 的两条规则在类型上钉死:可变性显式(*T vs *const T),
不许静默丢 const——想改,从一开始就得是 var9。
数组元素同理:&inventory_slots[2] 得 *u8(数组不存 const 值,
可变性由数组本身 var/const 决定),slot_ptr.* += 1 把第 3 格从 3 升到 410。
4.2 指针与整数互转:@intFromPtr / @ptrFromInt
系统级场景(内存映射硬件、调试生成点)要把指针当数字:
const spawn_point_ptr: *usize = @ptrFromInt(0xdeadbee0); // 数字 → 指针
const addr = @intFromPtr(spawn_point_ptr); // 指针 → 数字
try expect(addr == 0xdeadbee0);原书的警告:喂给 CPU 无效地址,程序崩得比 Boss 战翻车还快,用之前必须确认地址有效11。
4.3 volatile:别优化掉我
volatile 告诉编译器:这些读写必须按源代码原样发生,不许缓存、不许重排。 什么时候真需要?硬件寄存器(写某个地址 = 让电机转起来、点亮 LED)、 中断处理(别的执行流会改共享变量)、防编译器把「再读一次硬件寄存器」优化掉12。
划界(原书明写):volatile 不是并发工具——它防不住数据竞争、不提供原子性; 线程安全要用原子操作或同步原语(第 12 章),拿 volatile 当线程标志是 bug13。
4.4 指针转型:@ptrCast + @alignCast vs @bitCast
解析网络包/战利品盒:4 个原始字节,想读成一个 u32。
const loot_bytes = [_]u8{ 0x12, 0x34, 0x56, 0x78 };
const loot_value_ptr: *const u32 = @alignCast(@ptrCast(&loot_bytes[0]));
try expect(loot_value_ptr.* == 0x78563412); // 小端机器上的结果@ptrCast:「把这块内存重新解释成另一种指针类型」;@alignCast:保证地址满足目标类型的对齐要求(u32 通常要 4 字节对齐, 不满足会崩在未定义行为)14。
对照 @bitCast:不动内存,只给值换一副类型眼镜:
const item_id: u32 = 0x12345678;
const bytes = @bitCast([4]u8, item_id);
// bytes[0]==0x78, [1]==0x56, [2]==0x34, [3]==0x12(小端)选择判据(原书给了明确的)15:要 reinterpret 某个内存位置(硬件缓冲区、原始字节)
→ @ptrCast + @alignCast;要 reinterpret 一个值的比特(序列化、格式转换)
→ @bitCast,更简单更安全。
4.5 单项 vs 多项 vs 切片
| 类型 | 能做什么 | 不能做什么 |
|---|---|---|
*T 单项指针 | 解引用 ptr.* | 下标、指针算术(不假设后面还有元素) |
[*]T 多项指针 | 下标 ptr[i]、算术 ptr += 1 | 自带长度——走多远全靠你自觉 |
[]T 切片 | 下标(带边界检查)、.len、.ptr | 越界即 panic(安全模式) |
x_ptr[0..1] 能把单项指针变成 *[1]T(指向「恰好一个元素的数组」),
再顺势转成 [*]T16。指针算术的演示:ptr += 1 后 ptr[0] 从 1 变 217。
原书给切片起了它行业内通用的外号:胖指针(fat pointer)——指针 + 长度,
知道自己边界,越界被运行期抓住;裸指针(多项)没有这层保护,全靠自己18。
再记一条原书的纪律:别只改 slice.ptr 而不改 slice.len——那会造出一个
「声称的长度超过它能兜底的能力」的撒谎切片19。
5. 分配器协议与四个真分配器
5.1 协议与态度
Zig 没有「接口」关键字(没有 Java 式的 interface MemoryAllocator),
分配器是一套松协议:有 alloc、resize、free 三个方法的结构体,就算分配器20。
三个动作:
| 动作 | 干什么 |
|---|---|
alloc(T, n) | 要 n 个 T 的内存,成功返回切片,失败返回 error.OutOfMemory |
free(切片) | 归还。忘了 = 泄漏,「程序像宇宙恐怖一样膨胀直到吃掉所有内存」 |
resize(旧, 新大小) | 原地改大小;不行就返回 false,你得手动「新alloc→拷→free旧」 |
外加一对单件便捷函数:create(T) / destroy(指针)——本质是 alloc/free 一个元素21。
三个原书反复强调的态度,全部是第 1 章的复读:
- 没有默认分配器 lurking in the shadows——想要内存,自己决定从哪来、怎么还22;
- 分配失败是显式错误
error.OutOfMemory,没有静默超额承诺、没有 OOM killer 式的暗杀——优雅处理、重试、降级,是你的事23; - 手动管理换来确定性性能:没有 GC 的意外停顿,内存用量可以精细优化24。
最佳实践(本章最该背的一句):函数不要自己挑分配器,
把 std.mem.Allocator 作为参数收进来,让调用方决定25。
这一条让同一段代码可以在测试里用 testing allocator、在嵌入式里用 FBA、
在服务器里用 GPA——可测试性、可移植性都来自这一个参数。
5.2 选型:四个真分配器
| 分配器 | 一句话 | 什么时候用 |
|---|---|---|
| FixedBufferAllocator | 在你给的定长缓冲区上分配;满了就拒绝 | 内存需求可预测:嵌入式、游戏热循环、「我今天不要动态分配」 |
| GeneralPurposeAllocator (GPA) | 可配置的万用分配器,debug 带安全检查 | 没主意时的默认主力 |
| page_allocator | 直接向 OS 按页(通常 4 KB)要内存 | 大块分配;也是 GPA 的底层货源 |
| ArenaAllocator | 成批申请、一次性全还,不支持单独 free | 临时分配成堆、可一起释放:解析、批处理、每帧/每请求一干一净 |
FixedBufferAllocator 走查(原书示例):var buffer: [1024]u8 = undefined;
fba.init(&buffer)——之后 allocator.alloc(u8, 512) 就从这 1 KB 里划 512 字节26。
原书给的拼接函数 catAlloc 也是好例子:申请 a.len + b.len,
两段 @memcpy 拷进去——运行期拼字符串的最小完整实现27。
三个短板(原书明列):定死、不可跨线程共享、大小对齐全靠自己算28。
GPA 走查:声明时给一个配置结构体。常用的三个开关:
safety(抓 double free、非法访问;默认跟构建档走)、
enable_memory_limit + requested_memory_limit(超上限就 OOM 错误——给测试加内存预算的利器)、
retain_metadata + never_unmap(已释放的内存留着不还真删,专抓野指针写)29。
三种优化模式(Debug / ReleaseFast / ReleaseSmall)和全书的安全分档表一一对应30。
用完必须 defer _ = gpa.deinit()——它自己也要汇报泄漏。
page_allocator:Linux 用 mmap、Windows 用 VirtualAlloc,按页对齐, 及时把内存还给 OS31;GPA 的桶装不下的大块分配最终也委托给它32。
ArenaAllocator 是选型表里「生活方式」差别最大的:
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // 一次性还光,中途没有 free
const allocator = arena.allocator();
const m1 = try allocator.alloc(u8, 100);
const m2 = try allocator.alloc(u8, 200);
// 不用单独 free;defer 那句 deinit 全收机制:要一大块(块不够就链一个新块),在里面只 bump 不回收;
reset() 三种模式:free_all(全还)、retain_capacity(留着已用容量下次复用)、
retain_with_limit(留多少设上限)33。适合:解析例程、游戏每帧、Web 每请求——
「这一摊活结束就全不要」的场景34。不适合:需要单独释放某个对象的长寿命结构。
判断(我们的,不是书里的): 这张选型表是本章最实用的一页, 但原书没把「组合」说透:真实 Zig 程序的标配是分层—— arena 包着 page_allocator(小块走 arena、arena 的块向 page 要), GPA 底下也垫 page(大块直通)。选分配器不是四选一,是每层选一次。 另外 testing.allocator 在较新 Zig 版本中已转向基于 GPA 的实现 (补充,不在书里,版本相关)——第 7 章的 leak 检测和本章 GPA 的泄漏汇报, 底层是同一份记账逻辑。 如果错,会错在: 如果某版本 Zig 把 testing allocator 的实现细节再改, 「泄漏时报 [gpa]」这个字样可能消失;机制(对账 + 栈追踪)不会变。
6. 生命周期:剑与诅咒
6.1 三种存放位置,三种寿命
const manaPotion: f64 = 1.337; // 全局常量区:程序活着它就在
var monstersDefeated: usize = 0; // 全局数据区:同上
fn calculateDamage() u8 {
const swordDamage: u8 = 10; // 栈:函数返回即消失
return swordDamage; // 返回的是「拷贝」,安全
}局部变量随函数返回销毁,返回值(拷贝)没问题;返回指向栈内存的指针就是灾难35。
6.2 悬垂的两种典型死法(原书命名很妙)
被诅咒的剑——返回栈变量的指针:
fn cursedSword() *u8 {
var attackPower: u8 = 42;
return &attackPower; // 函数一结束,栈帧销毁,指针变野
}函数返回那一刻,指针所指已归还;再用它,行为未定义——「暂时能用,迟早反噬」36。
被诅咒的卷轴——返回指向栈数组的切片:
fn cursedScroll() []u8 {
var spell: [5]u8 = .{ 'F', 'i', 'r', 'e', '!' };
return spell[1..]; // 切片指向的数组随函数销毁,同样悬垂
}切片也救不了——它有长度、知道边界,但不知道「底层还活着吗」37。
正解(原书 enchantedSword):把数据拷到堆上再返回; 内存活在函数之外,但释放责任随之移交调用方38:
fn enchantedSword(allocator: std.mem.Allocator) ![]u8 {
var swordStats: [5]u8 = .{ 'S', 'l', 'a', 's', 'h' };
const statsCopy = try allocator.alloc(u8, swordStats.len);
@memcpy(statsCopy, &swordStats);
return statsCopy; // 调用方负责 free
}6.3 init/deinit 约定 + errdefer:主线例子
社区惯例(不是语言特性):资源型结构体自己封装「出生」和「入土」:
const Sword = struct {
stats: []u8,
pub fn init(allocator: std.mem.Allocator, stats: []const u8) !*Sword {
const sword_ptr = try allocator.create(Sword);
errdefer allocator.destroy(sword_ptr); // ② 后面失败:先造的结构体也回收
sword_ptr.stats = try allocator.alloc(u8, stats.len); // ③ 可能失败
@memcpy(sword_ptr.stats, stats);
return sword_ptr;
}
pub fn deinit(self: *Sword, allocator: std.mem.Allocator) void {
allocator.free(self.stats);
allocator.destroy(self);
}
};主线例子走查(申请两次,两次都可能失败):
| 步 | 发生 | errdefer 的作用 |
|---|---|---|
| ① create(Sword) | 结构体本体到手 | — |
| ② 登记 errdefer destroy | 「若出错,先收尸」 | — |
| ③ alloc stats 失败 | 触发 errdefer → 本体被 destroy | 部分分配不泄漏 |
| ③′ alloc stats 成功 | 无事 | — |
| 正常返回 | 调用方接剑 | errdefer 作废 |
原书的 Pro Tip 照收:分配内存时永远用 errdefer,任何部分分配在出错当下即回收39。
配套测试的两个分身(第 7 章见过第一个):
std.testing.allocator:记账。忘了 deinit,测试结束时报[gpa] (err): memory address 0x... leaked并附栈追踪40;std.testing.FailingAllocator:使坏。fail_index = 2表示第 3 次 alloc 必失败, 用来确定性测试「第 N 次分配失败」这条错误路径;还有配套的resize_fail_index、 以及alloc_index/allocated_bytes等记账指标41。 失败时能取getStackTrace()看是哪一行背叛了你42。
判断(我们的,不是书里的):
fail_index是本章最专业的一个工具, 它的本质是把「内存压力测试」从玄学变成确定性的:传统上「分配失败怎么办」 几乎无法测(你怎么让 malloc 恰好在第 3 次失败?),FailingAllocator 让每一条 错误路径都能被单测覆盖。这条机制单独看是个小工具,合起来看是 Zig 错误模型的又一块拼图: 因为错误是值、分配是显式 try,所以「注入失败」才变得可能。 异常系语言里给 new 注入失败,需要字节码级 mock 框架。 如果错,会错在: fail_index 按「alloc 调用次数」计数而不是按字节, 代码里一次 alloc 内部触发多次底层分配时(GPA 的桶策略),序号和直觉可能对不上, 需要结合 metrics 调试。
7. 安全观的正面回答:不比 Rust 不安全,只是不家长式
原书用一整节回应「没有 borrow checker 的 Zig 是不是更危险」。要点43:
- 指针默认非空,可空要显式
?*T并自带空指针检查——「安全路径即默认路径」; - 所有权规则口头化:你分配的归你;函数返回了指针,文档要写清谁释放; 借了内存,确认物主还活着。没有编译器强制执行,写错了 Zig 不保护你;
- Zig 靠契约 + 显式换安全:没有隐藏行为(没有隐式分配、隐式生命周期延长)、 每次分配的错误必须处理(没有静默超额承诺)、签名即合同。
- 原书的总结句:「Zig 不比 Rust 不安全,只是不那么家长式(paternalistic)。 Rust 假定你会犯错、在编译期拦你;Zig 信任你会认真思考、负责任地管理内存。」
这个立场自洽,但要读全:它成立的前提是写代码的人真的遵守契约。 借书库里别的拆解的话说,这是「把纪律写进文化,而不是写进编译器」的路线; 团队纪律松时,Zig 这条路的下限比 Rust 低——这是选型的真实权衡,不是语言优劣。
8. 零大小类型:不占字节的幽灵
零大小类型:@sizeOf(T) == 0——可以声明变量,但只有一个可能的值,
运行时不占任何字节44。常见成员:void、u0/i0、长度 0 的数组、
单标签枚举、字段全零大的结构体45。
真正好用的一个招(原书示例):用 void 把 HashMap 变成 Set——
AutoHashMap(i32, void) 的值槽位为空,编译器把存值的代码整个优化掉,
内存和指令双省46。配套区分:void ≠ anyopaque——void 是「没有数据」,
anyopaque 是「有数据但不告诉你是什么」,运行时是占地方的47。
另一个相关纪律:非 void 表达式不许被静默忽略(foo(); 返回 i32 会编译错误),
要么用 _ = foo(); 显式丢弃,要么真的用它;void 表达式无所谓,没有值可丢48。
9. 类型转换:三层分流
| 层 | 名字 | 谁做 | 例子 |
|---|---|---|---|
| ① | coercion(自动强转) | 编译器,保证安全时 | u8 → u16(加宽无损) |
| ② | 显式 cast | 你,内建函数 | @intCast(窄化,运行时查)、@floatFromInt、@intFromFloat(截断)、@bitCast(同尺寸重解释)、@ptrCast+@alignCast |
| ③ | peer type resolution | 编译器,跨分支协商 | if 两个分支返回不同类型,找公共父类型 |
①的场景(原书列表):整数浮点加宽、加 const、数组→切片、值→optional/错误并集、 同质元组→数组49。coercion 失败就是编译错误——收窄、float→int 这类有风险的操作, 编译器拒绝猜,逼你上 ②50。
②的判读规则(原书强调的可读性收益):看到 @intCast 就知道可能截断,
看到 @bitCast 就知道在重解释比特,看到普通赋值就知道安全——转换的「风险」
从行为变成了字面可见的信息51。C 指针特例:和 C 互操作时 Zig 放宽部分转换,
因为桥接代码难免要做些脏活——但 C 自己的坑照旧52。
③发生在 if/while/for/switch 表达式、多 break 的块、部分二元运算里:
各分支类型不同,编译器找一个所有分支都能安全 coerce 进去的公共类型53。常见结果:
i8 和 i16 的公共类型是 i16(加宽);数组和切片混合 → []const u8;
T 和 ?T 混合 → ?T;值和错误并集混合 → 错误并集54。找不到公共类型就编译错误,
那是你在催你显式 cast 或重想逻辑55。
10. 作者的判断与证据
可核的: 全部分配器代码示例与配置项、GPA 泄漏报文、FailingAllocator 的 fail_index/resize_fail_index/metrics、悬垂两例、void-as-Set 的测试、 coercion/cast/peer resolution 的分层——均可跑可验。
立场: 「Zig 不比 Rust 不安全,只是不家长式」43是作者最用力的一段论证; 它引了一篇外部博文(zackoverflow 的 unsafe-rust-vs-zig)做佐证56—— 注意那是博主观点,不是中立测评。「没有默认分配器 lurking in the shadows」 「You break it, you buy it」这类修辞服务的是同一种立场。
11. 边界与局限
| 缺什么 | 说明 |
|---|---|
| 没讲栈展开/调用约定的细节 | 栈帧具体怎么压、返回地址在哪,全章没有——对「懂内存」够用,对「懂机器」不够 |
| 没讲 memcpy/memmove 的坑 | @memcpy 重叠内存是未定义行为这类边界没提 |
| GPA 的内部结构(桶、slab)一笔带过 | 「small allocations use buckets」一句带过,调优时需要的细节都没有 |
| 分配器性能数字为零 | 四种分配器之间、与 malloc 之间的对比,没有一个基准 |
| ownership 没有形式化 | 「文档写清谁释放」依赖注释纪律;原书没给出所有权标注的惯例(如 Zig 社区后来讨论的 alloc/free 配对命名) |
| C 指针特例只给了一句 | [*c] T 类型(c pointer)的语义、与 *T 的互转规则,没展开——第 12 章 C 互操才会真正用到 |
12. 可带走的
主线例子,一行走完: Sword.init(allocator, "Victory!") → create 本体 →
errdefer 登记 → alloc stats(可能失败,errdefer 保部分分配)→ 返回;
defer sword.deinit(allocator) 对剑;GPA/testing.allocator 对账,
泄漏则 [gpa] (err): ... leaked + 栈追踪。
- 栈 = 函数的工作台(快、自动、8 MB 量级、编译期定大小);堆 = 租的仓库 (慢、自己还、可运行期定大小);栈溢出 vs 泄漏是两种死法;
- 指针三形态:
*T单项(无算术)、[*]T多项(有算术、无长度)、[]T切片 = 胖指针(指针+长度+边界检查); - 可变性写进指针类型(
*T/*const T),不许静默丢 const; volatile只保证读写按源代码发生,不是并发工具;- 转型分流:
@ptrCast+@alignCast重解释内存位置,@bitCast重解释值的比特; - 分配器三动作 alloc/free/resize;永远把 Allocator 当参数传, 调用方决定内存从哪来;
- 四选型:FBA(定长池)、GPA(万用,可配 safety/内存上限/retain)、 page(OS 按页直供)、arena(成批还,三种 reset 模式);真实程序分层组合;
- 悬垂两死法:返回栈变量指针、返回栈数组切片;正解 = 拷到堆、责任移交;
- init/deinit 约定 + errdefer:资源封装成对出现,部分分配在错误路径当场回收;
FailingAllocator 的
fail_index让「分配失败」路径可单测; - 类型转换三层:coercion(自动安全)→ 显式 cast(风险字面可见)→ peer type resolution(跨分支自动协商,协商不成编译错误)。
13. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 栈与 8 MB / 栈溢出 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:58(搜「guard page」) |
| 32 位 4 GB 挤压 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:88(搜「4 GB virtual address space」) |
| 其他内存分区 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:95(搜「global data, code, BSS」) |
| 指针的两个头疼 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:129(搜「two big headaches」) |
| 玩家血量指针走查 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:153(搜「npc_health: i32 = 100」) |
| 可变性写进指针类型 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:193(搜「resulting pointer type」) |
| 数组元素指针 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:230(搜「inventory_slots」) |
| @intFromPtr/@ptrFromInt | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:268(搜「0xdeadbee0」) |
| volatile | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:293(搜「real-world consequences」) · text/16-ch09-chapter-9-memory-management.txt:330(搜「not a tool for concurrency」) |
| ptrCast/alignCast 战利品盒 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:344(搜「loot_bytes」) · text/16-ch09-chapter-9-memory-management.txt:369(搜「4-byte boundary」) |
| @bitCast 道具 ID | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:375(搜「bitCast for decoding item ID」) |
| ptrCast vs bitCast 判据 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:410(搜「reinterpret a specific memory location」) |
| 单项 vs 多项指针 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:416(搜「single item」) · text/16-ch09-chapter-9-memory-management.txt:462(搜「ptr += 1」) |
| 胖指针 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:467(搜「fat pointer」) |
| 不许撒谎切片 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:456(搜「lying slice」) |
| 无接口、协议三动作 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:538(搜「relies on function pointers」) · text/16-ch09-chapter-9-memory-management.txt:552(搜「three big moves」) |
| OOM 显式错误 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:526(搜「error.OutOfMemory」) |
| 确定性性能 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:513(搜「deterministic performance」) |
| create/destroy | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:575(搜「create: Single-item allocation」) |
| 全局/局部寿命 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:603(搜「manaPotion」) |
| 悬垂双例 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:628(搜「cursedSword」) · text/16-ch09-chapter-9-memory-management.txt:639(搜「cursedScroll」) |
| enchantedSword 正解 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:652(搜「enchantedSword」) |
| Sword init/deinit + errdefer | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:672(搜「pub fn init(allocator」) · text/16-ch09-chapter-9-memory-management.txt:695(搜「Always use errdefer」) |
| testing.allocator 与传参惯例 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:700(搜「std.testing.allocator」) · text/16-ch09-chapter-9-memory-management.txt:705(搜「not to impose an allocator」) |
| [gpa] 泄漏报文 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:736(搜「leaked」) |
| FailingAllocator 与 fail_index | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:765(搜「failing_allocator」) · text/16-ch09-chapter-9-memory-management.txt:796(搜「fail_index」) · text/16-ch09-chapter-9-memory-management.txt:850(搜「alloc_index」) |
| FixedBufferAllocator | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:877(搜「first real allocator」) · text/16-ch09-chapter-9-memory-management.txt:898(搜「[1024]u8 = undefined」) |
| catAlloc 拼接 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:916(搜「catAlloc」) |
| FBA 三短板 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:974(搜「Achilles」) |
| GPA 与三种模式 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1013(搜「three optimization modes」) · text/16-ch09-chapter-9-memory-management.txt:1029(搜「GeneralPurposeAllocator(.{」) |
| GPA 配置项 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1054(搜「safety: Enables runtime checks」) |
| page_allocator | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1089(搜「unsung hero」) · text/16-ch09-chapter-9-memory-management.txt:1108(搜「std.heap.page_allocator」) |
| GPA 底下垫 page | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1124(搜「backing allocator」) |
| ArenaAllocator 机制与 reset | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1155(搜「playground」) · text/16-ch09-chapter-9-memory-management.txt:1189(搜「Buffer list」) · text/16-ch09-chapter-9-memory-management.txt:1196(搜「free_all」) |
| Arena 适用与短板 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1176(搜「Temporary allocations dominate」) · text/16-ch09-chapter-9-memory-management.txt:1266(搜「drawbacks」) |
| 选型军规 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1280(搜「choosing the right tool」) |
| 安全观对比 Rust | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1299(搜「non-null out of the gate」) · text/16-ch09-chapter-9-memory-management.txt:1354(搜「less paternalistic」) |
| 所有权两条 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1303(搜「you own what you allocate」) |
| 外部博文 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1360(搜「zackoverflow」) |
| 零大小类型清单 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1385(搜「@sizeOf(type) == 0」) · text/16-ch09-chapter-9-memory-management.txt:1388(搜「Common zero-sized」) |
| void-as-Set | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1407(搜「AutoHashMap(i32, void)」) |
| void vs anyopaque | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1422(搜「nonzero size at runtime」) |
| 忽略非 void 是编译错误 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1426(搜「compile error」) |
| coercion 与场景 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1477(搜「Automatically widens」) · text/16-ch09-chapter-9-memory-management.txt:1482(搜「Widening integers and floats」) |
| 显式 cast 清单 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1504(搜「will try to convert」) · text/16-ch09-chapter-9-memory-management.txt:1517(搜「Integer to float」) |
| 可读性收益 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1534(搜「risk of truncation」) |
| C 指针特例 | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1526(搜「more lenient」) |
| peer type resolution | Chapter 9: Memory Management | text/16-ch09-chapter-9-memory-management.txt:1549(搜「subtle dance」) · text/16-ch09-chapter-9-memory-management.txt:1574(搜「Numeric widening」) · text/16-ch09-chapter-9-memory-management.txt:1618(搜「cannot find a type」) |
Footnotes
-
出处:「Chapter 9: Memory Management」第 51–60 段(
text/16-ch09-chapter-9-memory-management.txt:53,搜「temporary workspace」)。 ↩ -
出处:同章第 57–61 段(
text/16-ch09-chapter-9-memory-management.txt:59,搜「8 MB」;text/16-ch09-chapter-9-memory-management.txt:58,搜「guard page」)。 ↩ -
出处:同章第 70–84 段(
text/16-ch09-chapter-9-memory-management.txt:71,搜「large warehouse」;text/16-ch09-chapter-9-memory-management.txt:77,搜「fragmentation」)。 ↩ -
出处:同章第 86–93 段(
text/16-ch09-chapter-9-memory-management.txt:88,搜「4 GB virtual address space」;text/16-ch09-chapter-9-memory-management.txt:93,搜「16 exabytes」)。 ↩ -
出处:同章第 94–100 段(
text/16-ch09-chapter-9-memory-management.txt:95,搜「global data, code, BSS」)。 ↩ -
出处:同章第 123–135 段(
text/16-ch09-chapter-9-memory-management.txt:129,搜「two big headaches」)。 ↩ -
出处:同章第 126–128 段(
text/16-ch09-chapter-9-memory-management.txt:128,搜「storage locker」)。 ↩ -
出处:同章第 147–178 段(
text/16-ch09-chapter-9-memory-management.txt:153,搜「npc_health: i32 = 100」;text/16-ch09-chapter-9-memory-management.txt:162,搜「player_health_ptr.* -= 20」)。 ↩ -
出处:同章第 189–211 段(
text/16-ch09-chapter-9-memory-management.txt:193,搜「resulting pointer type」;text/16-ch09-chapter-9-memory-management.txt:225,搜「Zig doesn’t let you」)。 ↩ -
出处:同章第 229–256 段(
text/16-ch09-chapter-9-memory-management.txt:230,搜「inventory_slots」;text/16-ch09-chapter-9-memory-management.txt:236,搜「slot_ptr.* += 1」)。 ↩ -
出处:同章第 267–291 段(
text/16-ch09-chapter-9-memory-management.txt:268,搜「0xdeadbee0」;text/16-ch09-chapter-9-memory-management.txt:290,搜「invalid addresses」)。 ↩ -
出处:同章第 292–328 段(
text/16-ch09-chapter-9-memory-management.txt:293,搜「real-world consequences」;text/16-ch09-chapter-9-memory-management.txt:327,搜「exactly as written」)。 ↩ -
出处:同章第 329–337 段(
text/16-ch09-chapter-9-memory-management.txt:330,搜「not a tool for concurrency」;text/16-ch09-chapter-9-memory-management.txt:336,搜「atomics or proper synchronization」)。 ↩ -
出处:同章第 339–369 段(
text/16-ch09-chapter-9-memory-management.txt:344,搜「loot_bytes」;text/16-ch09-chapter-9-memory-management.txt:369,搜「4-byte boundary」)。 ↩ -
出处:同章第 407–414 段(
text/16-ch09-chapter-9-memory-management.txt:410,搜「reinterpret a specific memory location」;text/16-ch09-chapter-9-memory-management.txt:412,搜「without touching memory」)。 ↩ -
出处:同章第 415–449 段(
text/16-ch09-chapter-9-memory-management.txt:439,搜「slice syntax on pointers」)。 ↩ -
出处:同章第 451–464 段(
text/16-ch09-chapter-9-memory-management.txt:462,搜「ptr += 1」)。 ↩ -
出处:同章第 465–470 段(
text/16-ch09-chapter-9-memory-management.txt:467,搜「fat pointer」)。 ↩ -
出处:同章第 455–457 段(
text/16-ch09-chapter-9-memory-management.txt:456,搜「lying slice」)。 ↩ -
出处:同章第 529–544 段(
text/16-ch09-chapter-9-memory-management.txt:538,搜「relies on function pointers」;text/16-ch09-chapter-9-memory-management.txt:538,搜「function pointers」)。 ↩ -
出处:同章第 552–592 段(
text/16-ch09-chapter-9-memory-management.txt:555,搜「alloc: Grabbing memory」;text/16-ch09-chapter-9-memory-management.txt:575,搜「create: Single-item allocation」)。 ↩ -
出处:同章第 509–511 段(
text/16-ch09-chapter-9-memory-management.txt:510,搜「no default allocator」)。 ↩ -
出处:同章第 520–527 段(
text/16-ch09-chapter-9-memory-management.txt:526,搜「error.OutOfMemory」)。 ↩ -
出处:同章第 513–516 段(
text/16-ch09-chapter-9-memory-management.txt:513,搜「deterministic performance」)。 ↩ -
出处:同章第 705–711 段(
text/16-ch09-chapter-9-memory-management.txt:705,搜「not to impose an allocator」;text/16-ch09-chapter-9-memory-management.txt:708,搜「as a parameter」)。 ↩ -
出处:同章第 877–910 段(
text/16-ch09-chapter-9-memory-management.txt:877,搜「first real allocator」;text/16-ch09-chapter-9-memory-management.txt:898,搜「[1024]u8 = undefined」)。 ↩ -
出处:同章第 912–938 段(
text/16-ch09-chapter-9-memory-management.txt:916,搜「catAlloc」)。 ↩ -
出处:同章第 972–980 段(
text/16-ch09-chapter-9-memory-management.txt:974,搜「Achilles」)。 ↩ -
出处:同章第 1008–1087 段(
text/16-ch09-chapter-9-memory-management.txt:1030,搜「.safety = true」;text/16-ch09-chapter-9-memory-management.txt:1054,搜「safety: Enables runtime checks」;text/16-ch09-chapter-9-memory-management.txt:1063,搜「enable_memory_limit」;text/16-ch09-chapter-9-memory-management.txt:1070,搜「retain_metadata」)。 ↩ -
出处:同章第 1013–1023 与 1130–1149 段(
text/16-ch09-chapter-9-memory-management.txt:1016,搜「Prioritizes safety」)。 ↩ -
出处:同章第 1089–1101 段(
text/16-ch09-chapter-9-memory-management.txt:1096,搜「mmap」;text/16-ch09-chapter-9-memory-management.txt:1098,搜「page size」)。 ↩ -
出处:同章第 1123–1128 段(
text/16-ch09-chapter-9-memory-management.txt:1124,搜「acts as the backing allocator」)。 ↩ -
出处:同章第 1155–1276 段(
text/16-ch09-chapter-9-memory-management.txt:1189,搜「linked list」;text/16-ch09-chapter-9-memory-management.txt:1196,搜「free_all」;text/16-ch09-chapter-9-memory-management.txt:1206,搜「ArenaAllocator.init」)。 ↩ -
出处:同章第 1173–1187 与 1257–1276 段(
text/16-ch09-chapter-9-memory-management.txt:1176,搜「Temporary allocations」;text/16-ch09-chapter-9-memory-management.txt:1268,搜「freed all at once」)。 ↩ -
出处:同章第 594–624 段(
text/16-ch09-chapter-9-memory-management.txt:603,搜「manaPotion」;text/16-ch09-chapter-9-memory-management.txt:614,搜「Returning a copy」)。 ↩ -
出处:同章第 626–636 段(
text/16-ch09-chapter-9-memory-management.txt:628,搜「cursedSword」;text/16-ch09-chapter-9-memory-management.txt:635,搜「pointer becomes invalid」)。 ↩ -
出处:同章第 638–648 段(
text/16-ch09-chapter-9-memory-management.txt:639,搜「cursedScroll」)。 ↩ -
出处:同章第 650–662 段(
text/16-ch09-chapter-9-memory-management.txt:652,搜「enchantedSword」;text/16-ch09-chapter-9-memory-management.txt:660,搜「outlive the function」)。 ↩ -
出处:同章第 664–697 段(
text/16-ch09-chapter-9-memory-management.txt:672,搜「pub fn init(allocator」;text/16-ch09-chapter-9-memory-management.txt:695,搜「Always use errdefer」)。 ↩ -
出处:同章第 699–763 段(
text/16-ch09-chapter-9-memory-management.txt:736,搜「leaked」;text/16-ch09-chapter-9-memory-management.txt:745,搜「pair init with deinit」)。 ↩ -
出处:同章第 765–866 段(
text/16-ch09-chapter-9-memory-management.txt:796,搜「fail_index」;text/16-ch09-chapter-9-memory-management.txt:825,搜「resize_fail_index」;text/16-ch09-chapter-9-memory-management.txt:850,搜「alloc_index」)。 ↩ -
出处:同章第 813–822 段(
text/16-ch09-chapter-9-memory-management.txt:817,搜「has_induced_failure」)。 ↩ -
出处:同章第 1293–1376 段(
text/16-ch09-chapter-9-memory-management.txt:1299,搜「non-null out of the gate」;text/16-ch09-chapter-9-memory-management.txt:1354,搜「less paternalistic」)。 ↩ ↩2 -
出处:同章第 1378–1398 段(
text/16-ch09-chapter-9-memory-management.txt:1385,搜「@sizeOf(type) == 0」)。 ↩ -
出处:同章第 1388–1395 段(
text/16-ch09-chapter-9-memory-management.txt:1390,搜「The 0-bit integers」;text/16-ch09-chapter-9-memory-management.txt:1390,搜「u0 and i0」)。 ↩ -
出处:同章第 1400–1419 段(
text/16-ch09-chapter-9-memory-management.txt:1407,搜「AutoHashMap(i32, void)」;text/16-ch09-chapter-9-memory-management.txt:1402,搜「turns it into a Set」)。 ↩ -
出处:同章第 1420–1423 段(
text/16-ch09-chapter-9-memory-management.txt:1422,搜「nonzero size at runtime」)。 ↩ -
出处:同章第 1425–1447 段(
text/16-ch09-chapter-9-memory-management.txt:1426,搜「compile error」;text/16-ch09-chapter-9-memory-management.txt:1434,搜「assign it to _」)。 ↩ -
出处:同章第 1472–1489 段(
text/16-ch09-chapter-9-memory-management.txt:1477,搜「Automatically widens」;text/16-ch09-chapter-9-memory-management.txt:1482,搜「Widening integers and floats」)。 ↩ -
出处:同章第 1490–1500 段(
text/16-ch09-chapter-9-memory-management.txt:1491,搜「refuses to coerce」)。 ↩ -
出处:同章第 1530–1543 段(
text/16-ch09-chapter-9-memory-management.txt:1534,搜「risk of truncation」)。 ↩ -
出处:同章第 1525–1528 段(
text/16-ch09-chapter-9-memory-management.txt:1526,搜「more lenient」)。 ↩ -
出处:同章第 1545–1561 段(
text/16-ch09-chapter-9-memory-management.txt:1549,搜「subtle dance」)。 ↩ -
出处:同章第 1572–1610 段(
text/16-ch09-chapter-9-memory-management.txt:1574,搜「Numeric widening」;text/16-ch09-chapter-9-memory-management.txt:1586,搜「Optionals and non-optional values」)。 ↩ -
出处:同章第 1617–1628 段(
text/16-ch09-chapter-9-memory-management.txt:1627,搜「prompting a」)。 ↩ -
出处:同章第 1359–1360 段(
text/16-ch09-chapter-9-memory-management.txt:1360,搜「zackoverflow」)。该文 URL(查阅于 2026-10-19):https://zackoverflow.dev/writing/unsafe-rust-vs-zig/ ↩