编程语言研究书库

内存管理 — 栈、堆、指针与分配器

内存管理 — 栈、堆、指针与分配器

这一章是全书最硬的一章,也是「没有 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 + 栈追踪。

  1. 栈 = 函数的工作台(快、自动、8 MB 量级、编译期定大小);堆 = 租的仓库 (慢、自己还、可运行期定大小);栈溢出 vs 泄漏是两种死法;
  2. 指针三形态:*T 单项(无算术)、[*]T 多项(有算术、无长度)、 []T 切片 = 胖指针(指针+长度+边界检查);
  3. 可变性写进指针类型(*T / *const T),不许静默丢 const;
  4. volatile 只保证读写按源代码发生,不是并发工具;
  5. 转型分流:@ptrCast+@alignCast 重解释内存位置,@bitCast 重解释值的比特;
  6. 分配器三动作 alloc/free/resize;永远把 Allocator 当参数传, 调用方决定内存从哪来;
  7. 四选型:FBA(定长池)、GPA(万用,可配 safety/内存上限/retain)、 page(OS 按页直供)、arena(成批还,三种 reset 模式);真实程序分层组合;
  8. 悬垂两死法:返回栈变量指针、返回栈数组切片;正解 = 拷到堆、责任移交;
  9. init/deinit 约定 + errdefer:资源封装成对出现,部分分配在错误路径当场回收; FailingAllocator 的 fail_index 让「分配失败」路径可单测;
  10. 类型转换三层:coercion(自动安全)→ 显式 cast(风险字面可见)→ peer type resolution(跨分支自动协商,协商不成编译错误)。

13. 原文地图

主题原书章原文位置
栈与 8 MB / 栈溢出Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:58(搜「guard page」)
32 位 4 GB 挤压Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:88(搜「4 GB virtual address space」)
其他内存分区Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:95(搜「global data, code, BSS」)
指针的两个头疼Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:129(搜「two big headaches」)
玩家血量指针走查Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:153(搜「npc_health: i32 = 100」)
可变性写进指针类型Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:193(搜「resulting pointer type」)
数组元素指针Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:230(搜「inventory_slots」)
@intFromPtr/@ptrFromIntChapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:268(搜「0xdeadbee0」)
volatileChapter 9: Memory Managementtext/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 Managementtext/16-ch09-chapter-9-memory-management.txt:344(搜「loot_bytes」) · text/16-ch09-chapter-9-memory-management.txt:369(搜「4-byte boundary」)
@bitCast 道具 IDChapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:375(搜「bitCast for decoding item ID」)
ptrCast vs bitCast 判据Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:410(搜「reinterpret a specific memory location」)
单项 vs 多项指针Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:416(搜「single item」) · text/16-ch09-chapter-9-memory-management.txt:462(搜「ptr += 1」)
胖指针Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:467(搜「fat pointer」)
不许撒谎切片Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:456(搜「lying slice」)
无接口、协议三动作Chapter 9: Memory Managementtext/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 Managementtext/16-ch09-chapter-9-memory-management.txt:526(搜「error.OutOfMemory」)
确定性性能Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:513(搜「deterministic performance」)
create/destroyChapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:575(搜「create: Single-item allocation」)
全局/局部寿命Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:603(搜「manaPotion」)
悬垂双例Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:628(搜「cursedSword」) · text/16-ch09-chapter-9-memory-management.txt:639(搜「cursedScroll」)
enchantedSword 正解Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:652(搜「enchantedSword」)
Sword init/deinit + errdeferChapter 9: Memory Managementtext/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 Managementtext/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 Managementtext/16-ch09-chapter-9-memory-management.txt:736(搜「leaked」)
FailingAllocator 与 fail_indexChapter 9: Memory Managementtext/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」)
FixedBufferAllocatorChapter 9: Memory Managementtext/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 Managementtext/16-ch09-chapter-9-memory-management.txt:916(搜「catAlloc」)
FBA 三短板Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:974(搜「Achilles」)
GPA 与三种模式Chapter 9: Memory Managementtext/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 Managementtext/16-ch09-chapter-9-memory-management.txt:1054(搜「safety: Enables runtime checks」)
page_allocatorChapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:1089(搜「unsung hero」) · text/16-ch09-chapter-9-memory-management.txt:1108(搜「std.heap.page_allocator」)
GPA 底下垫 pageChapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:1124(搜「backing allocator」)
ArenaAllocator 机制与 resetChapter 9: Memory Managementtext/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 Managementtext/16-ch09-chapter-9-memory-management.txt:1176(搜「Temporary allocations dominate」) · text/16-ch09-chapter-9-memory-management.txt:1266(搜「drawbacks」)
选型军规Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:1280(搜「choosing the right tool」)
安全观对比 RustChapter 9: Memory Managementtext/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 Managementtext/16-ch09-chapter-9-memory-management.txt:1303(搜「you own what you allocate」)
外部博文Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:1360(搜「zackoverflow」)
零大小类型清单Chapter 9: Memory Managementtext/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-SetChapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:1407(搜「AutoHashMap(i32, void)」)
void vs anyopaqueChapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:1422(搜「nonzero size at runtime」)
忽略非 void 是编译错误Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:1426(搜「compile error」)
coercion 与场景Chapter 9: Memory Managementtext/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 Managementtext/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 Managementtext/16-ch09-chapter-9-memory-management.txt:1534(搜「risk of truncation」)
C 指针特例Chapter 9: Memory Managementtext/16-ch09-chapter-9-memory-management.txt:1526(搜「more lenient」)
peer type resolutionChapter 9: Memory Managementtext/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

  1. 出处:「Chapter 9: Memory Management」第 51–60 段(text/16-ch09-chapter-9-memory-management.txt:53,搜「temporary workspace」)。 ↩

  2. 出处:同章第 57–61 段(text/16-ch09-chapter-9-memory-management.txt:59,搜「8 MB」;text/16-ch09-chapter-9-memory-management.txt:58,搜「guard page」)。 ↩

  3. 出处:同章第 70–84 段(text/16-ch09-chapter-9-memory-management.txt:71,搜「large warehouse」;text/16-ch09-chapter-9-memory-management.txt:77,搜「fragmentation」)。 ↩

  4. 出处:同章第 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」)。 ↩

  5. 出处:同章第 94–100 段(text/16-ch09-chapter-9-memory-management.txt:95,搜「global data, code, BSS」)。 ↩

  6. 出处:同章第 123–135 段(text/16-ch09-chapter-9-memory-management.txt:129,搜「two big headaches」)。 ↩

  7. 出处:同章第 126–128 段(text/16-ch09-chapter-9-memory-management.txt:128,搜「storage locker」)。 ↩

  8. 出处:同章第 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」)。 ↩

  9. 出处:同章第 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」)。 ↩

  10. 出处:同章第 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」)。 ↩

  11. 出处:同章第 267–291 段(text/16-ch09-chapter-9-memory-management.txt:268,搜「0xdeadbee0」;text/16-ch09-chapter-9-memory-management.txt:290,搜「invalid addresses」)。 ↩

  12. 出处:同章第 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」)。 ↩

  13. 出处:同章第 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」)。 ↩

  14. 出处:同章第 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」)。 ↩

  15. 出处:同章第 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」)。 ↩

  16. 出处:同章第 415–449 段(text/16-ch09-chapter-9-memory-management.txt:439,搜「slice syntax on pointers」)。 ↩

  17. 出处:同章第 451–464 段(text/16-ch09-chapter-9-memory-management.txt:462,搜「ptr += 1」)。 ↩

  18. 出处:同章第 465–470 段(text/16-ch09-chapter-9-memory-management.txt:467,搜「fat pointer」)。 ↩

  19. 出处:同章第 455–457 段(text/16-ch09-chapter-9-memory-management.txt:456,搜「lying slice」)。 ↩

  20. 出处:同章第 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」)。 ↩

  21. 出处:同章第 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」)。 ↩

  22. 出处:同章第 509–511 段(text/16-ch09-chapter-9-memory-management.txt:510,搜「no default allocator」)。 ↩

  23. 出处:同章第 520–527 段(text/16-ch09-chapter-9-memory-management.txt:526,搜「error.OutOfMemory」)。 ↩

  24. 出处:同章第 513–516 段(text/16-ch09-chapter-9-memory-management.txt:513,搜「deterministic performance」)。 ↩

  25. 出处:同章第 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」)。 ↩

  26. 出处:同章第 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」)。 ↩

  27. 出处:同章第 912–938 段(text/16-ch09-chapter-9-memory-management.txt:916,搜「catAlloc」)。 ↩

  28. 出处:同章第 972–980 段(text/16-ch09-chapter-9-memory-management.txt:974,搜「Achilles」)。 ↩

  29. 出处:同章第 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」)。 ↩

  30. 出处:同章第 1013–1023 与 1130–1149 段(text/16-ch09-chapter-9-memory-management.txt:1016,搜「Prioritizes safety」)。 ↩

  31. 出处:同章第 1089–1101 段(text/16-ch09-chapter-9-memory-management.txt:1096,搜「mmap」;text/16-ch09-chapter-9-memory-management.txt:1098,搜「page size」)。 ↩

  32. 出处:同章第 1123–1128 段(text/16-ch09-chapter-9-memory-management.txt:1124,搜「acts as the backing allocator」)。 ↩

  33. 出处:同章第 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」)。 ↩

  34. 出处:同章第 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」)。 ↩

  35. 出处:同章第 594–624 段(text/16-ch09-chapter-9-memory-management.txt:603,搜「manaPotion」;text/16-ch09-chapter-9-memory-management.txt:614,搜「Returning a copy」)。 ↩

  36. 出处:同章第 626–636 段(text/16-ch09-chapter-9-memory-management.txt:628,搜「cursedSword」;text/16-ch09-chapter-9-memory-management.txt:635,搜「pointer becomes invalid」)。 ↩

  37. 出处:同章第 638–648 段(text/16-ch09-chapter-9-memory-management.txt:639,搜「cursedScroll」)。 ↩

  38. 出处:同章第 650–662 段(text/16-ch09-chapter-9-memory-management.txt:652,搜「enchantedSword」;text/16-ch09-chapter-9-memory-management.txt:660,搜「outlive the function」)。 ↩

  39. 出处:同章第 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」)。 ↩

  40. 出处:同章第 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」)。 ↩

  41. 出处:同章第 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」)。 ↩

  42. 出处:同章第 813–822 段(text/16-ch09-chapter-9-memory-management.txt:817,搜「has_induced_failure」)。 ↩

  43. 出处:同章第 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

  44. 出处:同章第 1378–1398 段(text/16-ch09-chapter-9-memory-management.txt:1385,搜「@sizeOf(type) == 0」)。 ↩

  45. 出处:同章第 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」)。 ↩

  46. 出处:同章第 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」)。 ↩

  47. 出处:同章第 1420–1423 段(text/16-ch09-chapter-9-memory-management.txt:1422,搜「nonzero size at runtime」)。 ↩

  48. 出处:同章第 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 _」)。 ↩

  49. 出处:同章第 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」)。 ↩

  50. 出处:同章第 1490–1500 段(text/16-ch09-chapter-9-memory-management.txt:1491,搜「refuses to coerce」)。 ↩

  51. 出处:同章第 1530–1543 段(text/16-ch09-chapter-9-memory-management.txt:1534,搜「risk of truncation」)。 ↩

  52. 出处:同章第 1525–1528 段(text/16-ch09-chapter-9-memory-management.txt:1526,搜「more lenient」)。 ↩

  53. 出处:同章第 1545–1561 段(text/16-ch09-chapter-9-memory-management.txt:1549,搜「subtle dance」)。 ↩

  54. 出处:同章第 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」)。 ↩

  55. 出处:同章第 1617–1628 段(text/16-ch09-chapter-9-memory-management.txt:1627,搜「prompting a」)。 ↩

  56. 出处:同章第 1359–1360 段(text/16-ch09-chapter-9-memory-management.txt:1360,搜「zackoverflow」)。该文 URL(查阅于 2026-10-19):https://zackoverflow.dev/writing/unsafe-rust-vs-zig/ ↩

On this page