zig语言学习笔记——Zig 的三大内存区域

张开发
2026/6/9 21:27:59 15 分钟阅读

分享文章

zig语言学习笔记——Zig 的三大内存区域
Zig 的三大内存区域静态存储区 · 栈 · 堆Zig 是一门系统级编程语言没有隐藏的控制流、没有隐藏的内存分配、没有垃圾回收器。理解内存从哪来、到哪去是写好 Zig 的核心。Zig 程序中你打交道最多的就是这三个区域静态存储区Global / Constant Data → 栈Stack → 堆Heap一、静态存储区Global Data / Static Storage也叫全局数据区存放的是那些在编译期就完全确定的数据。什么东西放在这里类别示例字符串字面量hello world顶层const声明const pi 3.14159;顶层var声明var counter: u32 0;comptime变量编译期求值的常量struct内的var/const声明属于类型的静态成员关键特征嵌入在二进制文件中地址和程序一起加载到内存生命周期 整个程序的运行期从启动到退出一直存在大小在编译时确定不会增长也不会收缩字符串字面量的类型是[]const u8指向静态区的只读数据所以从函数返回字符串字面量是安全的——因为它不活在栈上fn sayHello() []const u8 { return hello world; // ✅ 安全hello world 在静态存储区 }你需要操心什么几乎什么都不用管。操作系统加载二进制时映射进去退出时回收。除了留意二进制体积别被大量嵌入数据撑大之外没有分配/释放的负担。二、栈Stack栈是函数调用的工作台。每个函数被调用时系统为它压入一个栈帧stack frame函数返回时栈帧弹出里面的一切自动销毁。什么东西放在栈上函数参数如fn add(x: u8, y: u8)里的x、y函数内声明的局部变量var/const只要大小编译期已知固定大小数组、struct实例等局部值const Point struct { x: f32, y: f32 }; pub fn main() void { var point: Point .{ .x 1.5, .y 2.7 }; // ← 在栈上 var buffer: [1024]u8 undefined; // ← 也在栈上 _ point; _ buffer; // main 返回时point 和 buffer 自动消失 }关键特征属性说明分配方式函数入口时自动预留空间调一个寄存器sp就搞定释放方式函数返回时自动回收 —零成本、确定性速度⚡ 极快比堆快一个数量级大小有限通常几 MB不能放超大对象生命周期绑定到作用域{}块 / 函数体⚠️ 经典坑不要返回栈上变量的指针fn createNode(value: i32) *Node { var node Node{ .value value, .left null, .right null }; return node; // ❌ node 在栈上函数返回后这块内存失效 // 返回的指针变成悬垂指针dangling pointer→ 未定义行为 }Zig不会阻止你写出这个 bug语言信任你但读取那个指针的结果是 UB。什么时候用栈只要大小编译期已知 生命周期不超过当前作用域就优先放栈。这是 Zig 的默认偏好。三、堆Heap堆是动态内存的领地。当你编译时不知道要多少内存或者数据的寿命必须超出当前函数就需要堆。怎么在 Zig 里分配堆内存Zig 的设计哲学是没有默认的全局分配器。需要分配的函数必须显式接收一个std.mem.Allocator参数const std import(std); pub fn main() !void { // 1. 选择一个分配器开发阶段常用 GeneralPurposeAllocator带泄漏检测 var gpa std.heap.GeneralPurposeAllocator(.{}){}; defer { const check gpa.deinit(); if (check .leak) std.debug.print(⚠️ Memory leak!\n, .{}); } const allocator gpa.allocator(); // 2. 在堆上分配 const buffer try allocator.alloc(u8, 1024); // 1KB on heap defer allocator.free(buffer); // ← YOU 负责释放 // 3. 使用... buffer[0] 42; }常见分配器速查分配器适用场景特点std.heap.page_allocator简单粗暴直接向 OS 要整页通常 4KB 起快但浪费GeneralPurposeAllocator日常开发通用、可检测泄漏、调试友好 ✅ 推荐起步用这个ArenaAllocator批量临时内存分配一大块退出时一次全释放不用逐个 freeFixedBufferAllocator嵌入式 / 约束环境在你给定的固定缓冲区上分配甚至可以在栈缓冲区上关键特征属性说明分配通过allocator.alloc()/allocator.create()显式请求释放你必须调allocator.free()/allocator.destroy()配合deferdefer保证即使中途报错也能释放 → 比裸 C 的malloc/free安全得多可以越界存活数据活多久你说了算不受函数返回影响代价慢于栈、可能碎片化、需要你脑子清醒四、三者对比一览静态存储区栈Stack堆Heap何时确定大小编译期编译期运行时谁管理分配/释放操作系统加载/卸载编译器自动函数进出你通过 Allocator速度—直接地址访问⚡ 最快较慢系统调用 簿记生命周期程序全程当前作用域结束即亡你控制直到你 free典型内容字符串字面量、全局 const局部变量、参数、小数组动态数组、变长数据、跨函数对象能返回指针吗✅ 安全一直在那❌ 悬垂指针✅ 安全只要你不提前 free五、决策口诀编译期已知 不出作用域 → 栈默认首选编译期已知 永远活着 → 静态区字面量 / 全局 const运行时才知道大小或需要活得比函数久 → 堆选一个 Allocator记得deferfreeZig 把这三块内存的边界画得非常清晰没有魔法也没有隐藏分配——这正是它debug your app, not your language knowledge的体现。

更多文章