Skip to content

Zig 变量教程

变量是内存存储单元,用于存储数据。

优先使用 const 而非 var:

  • 便于人类和计算机阅读代码
  • 创造更多优化机会
// 推荐:使用 const
const PI: f64 = 3.14159;
// 必要时才用 var
var counter: i32 = 0;
counter += 1;
  • 必须以字母或下划线开头
  • 后面可跟任意数量的字母数字字符或下划线
  • 不能与关键字冲突
  • 不允许遮蔽外部作用域的标识符
// 合法标识符
const normalName = 42;
const _private = 100;
const MAX_SIZE = 1024;
// 非法标识符(会导致编译错误)
// const 123name = 10; // 数字开头不允许

如果需要特殊名称(如链接外部库),使用 @"" 语法:

const @"identifier with spaces in it" = 0xff;
const @"1SmallStep4Man" = 112358;
// 用于链接 C 库
pub extern "c" fn @"error"() void;
pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int;
// 枚举中的特殊名称
const Color = enum {
red,
@"really red", // 带空格的枚举值
};
const color: Color = .@"really red";

3.1 容器级变量(Container Level Variables)

Section titled “3.1 容器级变量(Container Level Variables)”

特性:

  • 具有静态生命周期
  • 顺序无关(Zig 会智能分析依赖关系)
  • 延迟分析
  • 初始化值隐式是编译期已知的
// 注意:y 使用了 x,但 x 在 y 之后声明
// Zig 能正确分析这种依赖关系
var y: i32 = add(10, x);
const x: i32 = add(12, 34);
fn add(a: i32, b: i32) i32 {
return a + b;
}
// 测试:x = 12 + 34 = 46, y = 10 + 46 = 56
// x 是 const,编译期已知
// y 是 var,运行时已知

在结构体、联合、枚举或不透明类型中:

const S = struct {
var counter: i32 = 0; // 容器级变量
};
fn increment() i32 {
S.counter += 1;
return S.counter;
}

3.2 静态局部变量(Static Local Variables)

Section titled “3.2 静态局部变量(Static Local Variables)”

在函数内部使用容器来创建静态局部变量:

fn makeCounter() fn() i32 {
const Counter = struct {
var value: i32 = 0; // 静态局部变量
};
return fn() i32 {
Counter.value += 1;
return Counter.value;
};
}
// 使用示例
const counter = makeCounter();
counter(); // 返回 1
counter(); // 返回 2
counter(); // 返回 3

3.3 线程局部变量(Thread Local Variables)

Section titled “3.3 线程局部变量(Thread Local Variables)”

使用 threadlocal 关键字为每个线程创建独立的变量实例:

threadlocal var threadId: i32 = 0;
test "线程局部存储" {
// 每个线程都有自己独立的 threadId 副本
const thread1 = try std.Thread.spawn(.{}, worker, .{});
const thread2 = try std.Thread.spawn(.{}, worker, .{});
thread1.join();
thread2.join();
}
fn worker() void {
threadId = std.Thread.getCurrentId();
std.debug.print("Thread ID: {}\n", .{threadId});
}

重要限制:

  • 线程局部变量不能是 const
  • 在单线程构建中,线程局部变量被当作普通容器级变量处理

出现在函数、编译期块和 @cImport 块中:

fn example() void {
// 普通局部变量
var x: i32 = 10;
x += 5;
// const 局部变量
const y: i32 = 20;
// y = 30; // 错误:const 变量不能修改
// 如果 const 变量的初始值是编译期已知的,那么变量也是编译期已知的
const MAX_BUFFER_SIZE = 1024; // 编译期已知
}

使用 comptime 关键字声明编译期变量:

test "编译期变量" {
var runtimeVar: i32 = 1;
comptime var compileTimeVar: i32 = 1; // 编译期变量
runtimeVar += 1; // 运行时执行
compileTimeVar += 1; // 编译时执行
// 编译期变量的值在编译时已知
if (compileTimeVar != 2) {
@compileError("compileTimeVar 应该是 2");
}
// 编译期表达式中的所有变量隐式是编译期变量
comptime {
var a: i32 = 5; // 隐式 comptime
a *= 2; // 编译时计算
std.debug.assert(a == 10);
}
}

编译期变量的特点:

  • 所有加载和存储操作在语义分析期间发生
  • 可用于编译时计算和条件编译

使用 extern 或 @extern 链接其他对象导出的变量:

// 链接外部 C 变量
extern "c" var errno: c_int;
// 或使用 @extern
const externalVar = @extern(*i32, .{ .name = "global_counter" });

使用 export 或 @export 使变量在链接时对其他对象可用:

// 导出变量给其他模块使用
export var exported_counter: i32 = 0;
// 或使用 @export
var internal_counter: i32 = 0;
@export(internal_counter, .{ .name = "exported_name" });

重要要求: 导出/导入的变量类型必须是 C ABI 兼容的。

  1. 默认使用 const,只在需要修改时使用 var
  2. 遵循命名规范,使用 @"" 语法处理特殊情况
  3. 理解作用域:区分容器级、局部、线程局部变量
  4. 善用编译期变量:用于编译时计算和优化
  5. 注意线程安全:使用 threadlocal 处理线程特定数据
  6. 正确使用外部链接:确保类型兼容性
const std = @import("std");
// 容器级配置常量(编译期已知)
const Config = struct {
const MAX_USERS = 1000;
const TIMEOUT_MS = 5000;
// 运行时配置(可能从文件加载)
var debug_mode: bool = false;
};
// 线程局部存储
threadlocal var current_user_id: ?u32 = null;
// 静态局部计数器
fn makeUniqueId() u64 {
const State = struct {
var next_id: u64 = 1; // 静态局部变量
};
const id = State.next_id;
State.next_id += 1;
return id;
}
pub fn main() void {
// 局部变量
const user_count = 42;
var connection_active = true;
// 编译期计算
comptime {
const buffer_size = Config.MAX_USERS * @sizeOf(User);
std.debug.print("Buffer size needed: {}\n", .{buffer_size});
}
// 使用线程局部存储
current_user_id = 123;
std.debug.print("Unique IDs: {}, {}, {}\n", .{
makeUniqueId(),
makeUniqueId(),
makeUniqueId(),
});
}

通过掌握这些变量类型和用法,你可以编写出更安全、更高效、更易于维护的 Zig 代码。