Zig 教程
Zig 教程
Zig 是一门新兴的系统编程语言,由 Andrew Kelley 于 2015 年开始开发,目标是成为 C 语言的精神继承者:简单、可预测、无隐藏控制流,同时提供内存安全与强大的编译期能力。
Zig 的特点
- 无 GC 无运行时:直接编译为机器码,性能与 C 相当
- 内存安全:编译期检查悬垂指针、未定义行为,可显式管理内存
- 编译期计算:comptime 强大的元编程能力
- 无隐藏控制流:错误处理显式,不依赖异常
- 可交叉编译:内置交叉编译支持,无需工具链
- C 兼容:可直接包含 C 头文件,替代 C 的构建系统
Hello World
const std = @import("std");
pub fn main() !void {
std.debug.print("Hello, Zig!\n", .{});
}
学习路线
- 环境安装与基本语法
- 注释、数据类型、变量常量
- 循环、流程控制、运算符
- 函数、数组切片
- 结构体枚举、错误处理、内存管理
Zig 环境安装
Zig 环境安装
1. 安装方式
# macOS (Homebrew) brew install zig # Ubuntu / Debian sudo snap install zig --classic # 源码包 # 从 https://ziglang.org/download 下载 tar.xz tar -xf zig-linux-x86_64-0.13.0.tar.xz sudo mv zig-linux-x86_64-0.13.0 /opt/zig export PATH=$PATH:/opt/zig # Windows # 下载 zip 解压,加入 PATH
2. 验证安装
zig version # 0.13.0 zig help # 显示命令帮助
3. 项目初始化
# 创建新项目 mkdir hello-zig && cd hello-zig zig init # 生成的文件结构 # build.zig 构建脚本 # build.zig.zon 依赖清单 # src/ # main.zig 主程序 # root.zig 库入口 # 编译运行 zig build run
4. 常用命令
zig build # 构建项目 zig build run # 构建并运行 zig test src/main.zig # 运行测试 zig fmt src/main.zig # 格式化代码 zig run hello.zig # 直接编译运行单文件 zig build-exe hello.zig # 编译为可执行文件 zig cc file.c # 作为 C 编译器使用
5. IDE 支持
# VS Code 扩展 # "Zig Language" (ziglang.vscode-zig) # 语言服务器 zig build-exe -femit-bin=zls ... # 或安装 zls # brew install zls
Zig 基本语法
Zig 基本语法
1. Hello World 详解
// 导入标准库
const std = @import("std");
// pub 公开,main 是入口,!void 表示可能出错
pub fn main() !void {
// 格式化打印:.{} 是格式化参数占位
std.debug.print("Hello, Zig!\n", .{});
}
2. 语句与分号
// 表达式语句需要分号
const x = 5;
var y: i32 = 10;
y += 1;
// 块(block)可以返回值
const z = blk: {
const a = 1;
const b = 2;
break :blk a + b; // 块标签返回值
}; // z = 3
3. 变量声明
// const 不可变(推荐)
const name = "Zig";
const count: u32 = 100;
// var 可变
var score: i32 = 0;
score = 90;
// 常量表达式
const MAX = comptime 100;
// 导出符号
export fn add(a: i32, b: i32) i32 { return a + b; }
4. 导入
// 导入标准库模块
const std = @import("std");
const print = std.debug.print;
const ArrayList = std.ArrayList;
// 导入本地文件
const utils = @import("utils.zig");
// 导入包(依赖)
// build.zig.zon 中声明依赖后
const dep = @import("dep-name");
5. 空值 null 与 optional
// 可选类型(用 ? 前缀)
var maybe: ?i32 = null;
maybe = 5;
// 解包:捕获 else
if (maybe) |value| {
print("{d}\n", .{value});
} else {
print("无值\n", .{});
}
// 强制解包(危险,仅在确定非空时用)
const v = maybe.?;
Zig 注释
Zig 注释
1. 三种注释
// 普通注释
/// 文档注释:用于 API 文档,可被 zig 生成文档
/// 支持 markdown 语法
const Foo = struct {
/// 字段说明
x: i32,
};
//! 顶层文档注释:描述整个文件
//! 通常放在文件开头
// TODO: 待办标记
// FIXME: 需要修复
2. 文档生成
// 生成 HTML 文档
zig build-exe lib.zig -femit-docs
// 或 zig build docs(项目配置好时)
// 文档注释示例
/// 计算两个数的和
///
/// 参数:
/// - a: 第一个数
/// - b: 第二个数
///
/// 返回两数之和
pub fn sum(a: i32, b: i32) i32 {
return a + b;
}
3. 注释风格建议
- 用
///注释公开 API - 用
//!注释文件用途 - 普通
//解释代码逻辑 - 遵循 zig fmt 的注释格式
Zig 数据类型
Zig 数据类型
1. 整数类型
// 有符号: i8 i16 i32 i64 i128 const a: i32 = -100; // 无符号: u8 u16 u32 u64 u128 const b: u8 = 255; // 任意位宽整数 const c: u7 = 100; // 7 位无符号 // usize 指针宽度(平台相关) const n: usize = 100; // 强制类型转换 const x: i32 = @intCast(b); // u8 → i32 const y: u8 = @intCast(a); // 需要安全转换 const z: u8 = @truncate(300); // 截断:44
2. 浮点类型
const f1: f32 = 3.14; const f2: f64 = 3.14159265358979; const f3: f16 = 1.5; // 特殊值 const inf = std.math.inf(f64); const nan = std.math.nan(f64); // 转换 const i: i32 = @intFromFloat(3.9); // 3(截断) const f: f64 = @floatFromInt(100); // 100.0
3. 布尔与字符
const ok: bool = true; const no: bool = false; // 逻辑运算 const r1 = true and false; // false const r2 = true or false; // true const r3 = !true; // false // 字符:u21 存储 Unicode 码点 const ch: u21 = 'A'; const chinese: u21 = '中'; // 字符串字面量 const s: []const u8 = "hello"; const cstr: [:0]const u8 = "c风格";
4. 数组与切片
// 数组(定长)
const arr: [5]i32 = [_]i32{ 1, 2, 3, 4, 5 };
const len = arr.len; // 5
const first = arr[0];
// 切片(动态长度视图)
const slice: []const i32 = &arr;
const sub = arr[1..3]; // {2, 3}
// 字符串是字节切片
const s = "hello";
const byte_count = s.len; // 5
5. 复合类型
// 结构体
const Point = struct { x: f64, y: f64 };
const p = Point{ .x = 1.0, .y = 2.0 };
// 枚举
const Color = enum { red, green, blue };
const c = Color.red;
// 联合(标签联合)
const Value = union(enum) {
int: i32,
float: f64,
str: []const u8,
};
// 可选
var opt: ?i32 = null;
// 错误联合
fn f() error{OutOfMemory}!void {}
Zig 变量和常量
Zig 变量和常量
1. const 常量
// const:不可变,编译期已知(多数情况下)
const pi = 3.14159;
const name: []const u8 = "Zig";
const arr = [_]i32{ 1, 2, 3 };
// comptime 强制编译期计算
const table = comptime blk: {
var t: [10]i32 = undefined;
for (0..10) |i| {
t[i] = @intCast(i * i);
}
break :blk t;
};
2. var 变量
// var:可变变量
var count: i32 = 0;
count += 1;
// 需要初始化或明确 undefined
var buffer: [256]u8 = undefined; // 未初始化(危险,慎用)
// 变量遮蔽
const x = 1;
{
const x = 2; // 内层遮蔽外层
print("{d}\n", .{x}); // 2
}
print("{d}\n", .{x}); // 1
3. 类型推导
// 自动推断类型
const a = 42; // comptime_int
const b: i32 = 42;
const c = 3.14; // comptime_float
const d: f64 = 3.14;
// 数组类型推导
const arr = [_]u8{ 1, 2, 3 }; // [3]u8
// 结构体类型
const p = .{ .x = 1, .y = 2 }; // 匿名结构体
4. 顶层与局部作用域
// 顶层(文件级)
const TOP = 100;
fn myFunc() void {
const local = 10; // 函数内局部
{
const inner = 20; // 块内
}
// inner 在这里不可见
}
// 容器级常量
const Config = struct {
const default_port: u16 = 8080;
};
5. 变量安全建议
- 默认使用 const,确需修改才用 var
- 避免 undefined 未初始化(易出未定义行为)
- 整数运算注意溢出(可开启溢出检查)
- 使用 @intCast 显式类型转换
Zig 循环
Zig 循环
1. while 循环
const std = @import("std");
// 基本 while
var i: i32 = 0;
while (i < 5) : (i += 1) {
std.debug.print("{d}\n", .{i});
}
// 无 continue 表达式的写法
var j: i32 = 0;
while (j < 3) {
std.debug.print("j={d}\n", .{j});
j += 1;
}
// while 可选
while (maybe) |value| {
// value 已解包
} else {
// 无值
}
2. for 循环
// 遍历数组
const arr = [_]i32{ 10, 20, 30 };
for (arr) |value| {
std.debug.print("{d}\n", .{value});
}
// 带索引
for (arr, 0..) |value, index| {
std.debug.print("[{d}] = {d}\n", .{ index, value });
}
// 遍历切片
const slice: []const i32 = &arr;
for (slice[1..]) |v| { ... }
// 多个数组并行
const a = [_]i32{ 1, 2, 3 };
const b = [_]i32{ 4, 5, 6 };
for (a, b) |x, y| {
std.debug.print("{d}+{d}={d}\n", .{ x, y, x + y });
}
3. break 与 continue
var n: i32 = 0;
while (n < 10) : (n += 1) {
if (n == 3) continue; // 跳过 3
if (n == 7) break; // 到 7 退出
std.debug.print("{d}\n", .{n});
}
// 标签跳出多层循环
outer: while (true) {
inner: while (true) {
break :outer; // 跳出外层
}
}
4. 循环返回值
// while 可以带 else 返回值
const found = blk: {
var i: usize = 0;
while (i < arr.len) : (i += 1) {
if (arr[i] == target) break :blk i;
}
break :blk null; // 未找到
};
5. 循环性能与边界
// 边界检查:Zig 默认检查数组越界
// const v = arr[10]; // 编译/运行时报错
// 关闭安全检查(不推荐,除非确定安全)
// const v = arr[10] catch unreachable;
// 无限循环
while (true) {
// 常用于事件循环
}
Zig 流程控制
Zig 流程控制
1. if 表达式
const std = @import("std");
const x: i32 = 10;
// 基本 if
if (x > 0) {
std.debug.print("正数\n", .{});
} else if (x < 0) {
std.debug.print("负数\n", .{});
} else {
std.debug.print("零\n", .{});
}
// if 是表达式,可赋值
const label = if (x > 0) "positive" else "non-positive";
// 可选值解包
var maybe: ?i32 = 5;
if (maybe) |v| {
std.debug.print("值: {d}\n", .{v});
} else {
std.debug.print("无值\n", .{});
}
2. switch 表达式
const Color = enum { red, green, blue };
const c = Color.green;
const rgb = switch (c) {
.red => 0xFF0000,
.green => 0x00FF00,
.blue => 0x0000FF,
};
// 数字 switch
const n: u8 = 3;
const word = switch (n) {
0 => "zero",
1 => "one",
2, 3, 4 => "small", // 多值合并
else => "big", // 默认
};
// 区间匹配
const score: i32 = 85;
const grade = switch (score) {
0...59 => "D",
60...79 => "C",
80...89 => "B",
90...100 => "A",
else => unreachable,
};
3. switch 穷尽性
// 枚举 switch 必须覆盖所有分支(否则编译错误)
const Direction = enum { north, south, east, west };
fn describe(d: Direction) []const u8 {
return switch (d) {
.north => "北",
.south => "南",
.east => "东",
.west => "西",
}; // 少一个分支会编译失败!
}
4. 错误处理控制流
// if 捕获错误
if (mayFail()) |value| {
// 成功分支
} else |err| {
// 错误分支
std.debug.print("错误: {}\n", .{err});
}
// switch 处理错误
const result = mayFail() catch {
std.debug.print("出错,回退\n", .{});
return;
};
5. 控制流最佳实践
- 优先使用表达式形式(if/switch 返回值)
- 枚举 switch 保持穷尽
- 可选值用 if 解包而非 .?(强制)
- 用 catch 或 errdefer 显式处理错误
Zig 运算符
Zig 运算符
1. 算术运算符
const a: i32 = 7; const b: i32 = 3; const sum = a + b; // 10 const diff = a - b; // 4 const prod = a * b; // 21 const quot = a / b; // 2(整数除法截断) const rem = a % b; // 1(取余) // 浮点运算 const x: f64 = 7.0; const y: f64 = 3.0; const q = x / y; // 2.333... // 复合赋值 var n: i32 = 5; n += 3; // 8 n -= 2; // 6 n *= 4; // 24 n /= 6; // 4 n %= 3; // 1 // 幂与根(标准库) const pow = std.math.pow(f64, 2.0, 10.0); // 1024.0 const sqrt = std.math.sqrt(16.0); // 4.0 const abs = std.math.absInt(-5) catch 0; // 5
2. 位运算符
const m: u8 = 0b1100; const n: u8 = 0b1010; const and = m & n; // 0b1000 const or = m | n; // 0b1110 const xor = m ^ n; // 0b0110 const not = ~m; // 取反 const shl = m << 1; // 0b11000(左移) const shr = m >> 1; // 0b0110(右移)
3. 比较运算符
const a = 5; const b = 10; a == b // false a != b // true a < b // true a <= b // true a > b // false a >= b // false // 浮点比较(注意 NaN) const f = std.math.nan(f64); f == f // false(NaN 不等于自身) // 用 std.math.isNan(f) 判断
4. 逻辑运算符
// and / or / not
const ok = true;
const ready = false;
ok and ready // false
ok or ready // true
!ok // false
// 短路求值
// 与 C 不同,Zig 的 and/or 总是求值两侧?
// 不,zig 中 and/or 不短路!注意:
// 需要短路时用 if/while 嵌套:
if (a != null and a.? > 0) ... // 仍会求值 a.?
// 安全写法
if (a) |v| {
if (v > 0) { ... }
}
5. 类型相关运算符
// 取地址
var x: i32 = 5;
const ptr: *i32 = &x;
ptr.* = 10; // 解引用修改
// 数组元素地址
const arr = [_]i32{ 1, 2, 3 };
const p = &arr[1];
// 类型转换
const i: i32 = @intCast(100);
const f: f64 = @floatFromInt(100);
const u: u8 = @truncate(300); // 44(截断)
// 大小
const size = @sizeOf(i32); // 4
const align = @alignOf(i32); // 4
Zig 函数
Zig 函数
1. 函数定义
const std = @import("std");
// 无返回值
fn greet(name: []const u8) void {
std.debug.print("Hello, {s}!\n", .{name});
}
// 带返回值
fn add(a: i32, b: i32) i32 {
return a + b;
}
// 单表达式函数
fn square(x: i32) i32 {
return x * x;
}
// 调用
pub fn main() void {
greet("Zig");
const s = add(2, 3);
std.debug.print("和: {d}\n", .{s});
}
2. 参数与返回类型
// 切片参数(推荐,不复制数据)
fn sum(items: []const i32) i32 {
var total: i32 = 0;
for (items) |v| total += v;
return total;
}
// 返回多个值(元组/结构体)
fn divmod(a: i32, b: i32) struct { quot: i32, rem: i32 } {
return .{ .quot = a / b, .rem = a % b };
}
// 可选返回
fn find(arr: []const i32, target: i32) ?usize {
for (arr, 0..) |v, i| {
if (v == target) return i;
}
return null;
}
// 错误返回
fn parseNumber(s: []const u8) !i32 {
return std.fmt.parseInt(i32, s, 10);
}
3. 错误联合与 catch
fn risky() !i32 {
if (true) return error.Failed;
return 42;
}
pub fn main() void {
// catch 提供回退值
const v1 = risky() catch 0;
std.debug.print("{d}\n", .{v1}); // 0
// 传播错误
fn wrapper() !void {
const v = try risky(); // 成功则继续,失败则返回错误
std.debug.print("{d}\n", .{v});
}
// 处理错误
if (risky()) |value| {
std.debug.print("成功: {d}\n", .{value});
} else |err| {
std.debug.print("错误: {}\n", .{err});
}
}
4. 泛型函数
// 用 anytype 或 comptime 类型参数
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
// 调用时自动实例化
const m1 = max(i32, 3, 5);
const m2 = max(f64, 1.5, 2.5);
// 对任意类型集合求和
fn sumAll(comptime T: type, items: []const T) T {
var total: T = 0;
for (items) |v| total += v;
return total;
}
5. 内联函数与闭包
// inline 内联
inline fn fastMul(a: i32, b: i32) i32 {
return a * b;
}
// 函数指针
const handler = fn (i32) void;
fn process(h: handler, value: i32) void {
h(value);
}
// 编译期执行
fn tableAt(comptime i: usize) i32 {
return @intCast(i * i);
}
const value = comptime tableAt(5); // 25
Zig 数组和切片
Zig 数组和切片
1. 数组(Array)
const std = @import("std");
// 定长数组
const a: [5]i32 = [_]i32{ 1, 2, 3, 4, 5 };
const b = [3]u8{ 10, 20, 30 };
// 数组属性
const len = a.len; // 5
const first = a[0]; // 1
const last = a[a.len - 1];
// 修改元素(需 var)
var c = [_]i32{ 0, 0, 0 };
c[1] = 100;
// 默认值数组
var zeros = [_]i32{0} ** 10; // 10 个 0
// {0} ** 10 表示重复 10 次
2. 数组遍历与操作
// 遍历
for (a) |v| {
std.debug.print("{d} ", .{v});
}
// 切片视图
const slice = a[1..3]; // {2, 3}
// 复制
var dst: [5]i32 = undefined;
@memcpy(&dst, &a);
// 比较
const eq = std.mem.eql(i32, &a, &a);
// 排序(标准库)
var arr = [_]i32{ 3, 1, 2 };
std.mem.sort(i32, &arr, {}, std.sort.asc(i32));
3. 切片(Slice)
// 切片是对数组的视图,包含指针和长度
const s: []const i32 = &a;
const len = s.len; // 5
const item = s[0];
// 从数组创建切片
const s2 = a[0..3]; // 前 3 个元素
const s3 = a[2..]; // 从索引 2 到结尾
const all = a[0..]; // 整个数组
// 可写切片
var arr2 = [_]i32{ 1, 2, 3 };
const ws: []i32 = &arr2;
ws[0] = 99; // 修改原数组
4. 字符串处理
// 字符串字面量是 []const u8
const hello: []const u8 = "hello";
const len = hello.len; // 5(字节数)
// 拼接
var buf: [32]u8 = undefined;
const s = std.fmt.bufPrint(&buf, "{s}-{d}", .{ "id", 42 }) catch "";
// s = "id-42"
// 分割
var it = std.mem.splitSequence(u8, "a,b,c", ",");
while (it.next()) |part| {
std.debug.print("{s}\n", .{part});
}
// 转数字
const n = std.fmt.parseInt(i32, "42", 10) catch 0;
5. 动态数组(ArrayList)
// 堆分配的动态数组
var list = std.ArrayList(i32).init(std.heap.page_allocator);
defer list.deinit(); // 释放
try list.append(1);
try list.append(2);
try list.appendSlice(&[_]i32{ 3, 4 });
for (list.items) |v| {
std.debug.print("{d} ", .{v});
}
list.clearRetainingCapacity(); // 清空
Zig 结构体和枚举
Zig 结构体和枚举
1. 结构体定义
const std = @import("std");
const Point = struct {
x: f64,
y: f64,
// 方法(第一个参数 self)
fn distance(self: Point, other: Point) f64 {
const dx = self.x - other.x;
const dy = self.y - other.y;
return std.math.sqrt(dx * dx + dy * dy);
}
// 静态方法
fn origin() Point {
return .{ .x = 0, .y = 0 };
}
};
pub fn main() void {
const p1 = Point{ .x = 0, .y = 0 };
const p2 = Point{ .x = 3, .y = 4 };
std.debug.print("距离: {d}\n", .{p1.distance(p2)});
const o = Point.origin();
std.debug.print("原点: ({d},{d})\n", .{ o.x, o.y });
}
2. 默认值与可选字段
const Config = struct {
host: []const u8 = "localhost",
port: u16 = 8080,
debug: bool = false,
};
// 只覆盖需要的字段
const dev = Config{ .port = 9090, .debug = true };
const prod = Config{}; // 全默认
// 可选字段
const Opt = struct {
name: ?[]const u8 = null,
};
3. 枚举定义
const Color = enum {
red,
green,
blue,
// 枚举方法
fn hex(self: Color) u32 {
return switch (self) {
.red => 0xFF0000,
.green => 0x00FF00,
.blue => 0x0000FF,
};
}
// 从整数创建
fn fromCode(code: u8) ?Color {
return std.meta.intToEnum(Color, code) catch null;
}
};
// 使用
const c = Color.red;
std.debug.print("{X}\n", .{c.hex()}); // FF0000
// 遍历枚举
inline for (@typeInfo(Color).@"enum".fields) |field| {
std.debug.print("{s}\n", .{field.name});
}
4. 带值的枚举
const HttpStatus = enum(u16) {
ok = 200,
not_found = 404,
error = 500,
fn isOk(self: HttpStatus) bool {
return self == .ok;
}
};
const code: u16 = @intFromEnum(HttpStatus.not_found); // 404
const status: HttpStatus = @enumFromInt(404);
5. 联合体与标签联合
// 简单联合
const U = union {
int: i32,
float: f64,
};
// 标签联合(推荐,安全)
const Value = union(enum) {
int: i32,
float: f64,
text: []const u8,
none,
fn describe(self: Value) void {
switch (self) {
.int => |v| std.debug.print("整数: {d}\n", .{v}),
.float => |v| std.debug.print("浮点: {d}\n", .{v}),
.text => |s| std.debug.print("文本: {s}\n", .{s}),
.none => std.debug.print("空\n", .{}),
}
}
};
const v = Value{ .int = 42 };
v.describe();
zig 错误处理
Zig 错误处理
Zig 不使用异常,错误通过返回值显式传播,清晰可控、零开销。
1. 错误集与错误联合
const std = @import("std");
// 错误集定义
const MyError = error{
OutOfMemory,
InvalidInput,
NotFound,
};
// 错误联合类型:成功返回 i32,失败返回错误
fn divide(a: i32, b: i32) MyError!i32 {
if (b == 0) return error.InvalidInput;
return a / b;
}
2. try 与 catch
// try:失败则提前返回错误
fn caller() !void {
const result = try divide(10, 2);
std.debug.print("{d}\n", .{result});
}
// catch:提供回退值
const v1 = divide(10, 0) catch 0;
std.debug.print("{d}\n", .{v1}); // 0
// catch 块
const v2 = divide(10, 0) catch blk: {
std.debug.print("计算失败,回退\n", .{});
break :blk -1;
};
// 忽略错误(谨慎)
_ = divide(1, 0) catch {};
3. if/switch 处理错误
// if 捕获
if (divide(10, 0)) |value| {
std.debug.print("成功: {d}\n", .{value});
} else |err| {
std.debug.print("错误: {}\n", .{err});
}
// switch 按错误类型分支
switch (divide(10, 0)) {
error.InvalidInput => std.debug.print("输入错误\n", .{}),
else => |e| std.debug.print("其他错误: {}\n", .{e}),
}
4. errdefer 与 defer
// defer:函数退出时执行(无论成败)
fn readFile() ![]u8 {
const file = try std.fs.cwd().openFile("data.txt", .{});
defer file.close(); // 退出时关闭
// errdefer:仅出错时执行
const buf = try file.readToEndAlloc(
std.heap.page_allocator,
1024 * 1024
);
errdefer std.heap.page_allocator.free(buf); // 出错释放
return buf;
}
5. 错误处理模式
// 捕获特定错误
fn safeDivide(a: i32, b: i32) i32 {
return divide(a, b) catch |err| switch (err) {
error.InvalidInput => 0,
else => unreachable, // 其他错误不可能
};
}
// 错误合并(不同错误集)
fn multi() error{A}!void {}
fn multi2() error{B}!void {}
fn combined() error{A || B}!void {
try multi();
try multi2();
}
// 全局分配器错误处理
fn alloc() ![]u8 {
const buf = try std.heap.page_allocator.alloc(u8, 100);
defer std.heap.page_allocator.free(buf);
return buf;
}
6. 错误处理最佳实践
- 错误信息具体:error.InvalidInput 优于 error.Failed
- 用 try 传播、catch 兜底、switch 分类处理
- 用 errdefer 做资源清理
- 避免吞掉错误(空 catch)
Zig 内存管理
Zig 内存管理
Zig 没有 GC 也没有自动内存管理,内存分配与释放全部显式,配合编译期检查保证安全。
1. 分配器概念
const std = @import("std");
// 常用分配器
std.heap.page_allocator // 直接调用操作系统,慢但简单
std.heap.c_allocator // 包装 C 的 malloc/free
std.heap.GeneralPurposeAllocator(.{}) // 通用分配器(带检测)
std.heap.ArenaAllocator // 竞技场:一次性释放
// 推荐:开发时用带检测的分配器
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer _ = gpa.deinit(); // 检测内存泄漏,泄漏会报错
2. 分配与释放
// 分配单个值 const p = try allocator.create(i32); p.* = 42; allocator.destroy(p); // 释放 // 分配数组 const arr = try allocator.alloc(i32, 100); defer allocator.free(arr); // 释放 // 重新分配(扩容) var buf = try allocator.alloc(u8, 100); buf = try allocator.realloc(buf, 200); defer allocator.free(buf); // 分配并填充 const str = try allocator.dupe(u8, "hello"); defer allocator.free(str);
3. 内存安全原则
// 原则:谁分配,谁释放
// 使用 defer 保证释放
fn process() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const a = gpa.allocator();
const buf = try a.alloc(u8, 1024);
defer a.free(buf); // 函数退出自动释放
// 使用 buf ...
}
4. 悬垂指针检测
// 释放后使用会被编译器/分配器检测 const p = try allocator.create(i32); p.* = 10; allocator.destroy(p); // p.* = 20; // 错误!已释放 // 用可选指针表示可能失效 var ptr: ?*i32 = try allocator.create(i32); ptr.?.* = 10; allocator.destroy(ptr.?); ptr = null; // 标记失效
5. ArenaAllocator 场景
// 大量短期分配,结束时一次性释放
fn handleRequest() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // 一次性释放所有
const a = arena.allocator();
// 任意多次分配,不用单独 free
const s1 = try a.dupe(u8, "request data");
const s2 = try a.dupe(u8, "more data");
// ...
}
6. 内存泄漏检测
// 用 GeneralPurposeAllocator 自动检测
test "memory safety" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); // 若有泄漏,这里报错
const a = gpa.allocator();
const buf = try a.alloc(u8, 100);
// 忘记 free → 测试失败并报告泄漏
_ = buf;
}
// 运行测试
// zig test src/main.zig
第 1 / 14 节