Ziglings 笔记 66: 编译期与运行时 (Comptime vs Runtime)
两个世界
在 Ziglings 的第 66 个练习中,我们正式接触了 Zig 的杀手级特性 —— Comptime。
在大多数语言中,写代码就是为了在运行时 (Runtime) 执行。但在 Zig 中,我们有一半的工作其实是在编译时 (Compile Time) 完成的。理解这两个“世界”的边界,是掌握 Zig 的关键。
挑战:类型的不确定性
代码中有两个常量和两个变量,被赋予了相同的数值。
常量 const 能够自动推导类型,但变量 var 却报错了。为什么编译器这么双标?
const const_int = 12345; // ✅ 正常工作
var var_int = 12345; // ❌ 编译错误!
解决方案
我们需要为 var 变量显式指定运行时类型:
const print = @import("std").debug.print;
pub fn main() void {
// === 编译期世界 ===
// 这些值只存在于编译器内部。
// 类型推导为 comptime_int / comptime_float (任意精度)。
const const_int = 12345;
const const_float = 987.654;
print("Immutable: {}, {d:.3}; ", .{ const_int, const_float });
// === 运行时世界 ===
// 这里的变量需要在程序运行时的内存中占位。
// 内存必须有固定大小,所以必须显式标注类型 (u32, f32)。
// 修复 1: 指定整数类型 u32
var var_int: u32 = 12345;
// 修复 2: 指定浮点类型 f32
var var_float: f32 = 987.654;
// 可以在运行时修改它们
var_int = 54321;
var_float = 456.789;
print("Mutable: {}, {d:.3}; ", .{ var_int, var_float });
// 见证真相的时刻:查看类型
// 输出: comptime_int, comptime_float, u32, f32
print("Types: {}, {}, {}, {}\n", .{
@TypeOf(const_int),
@TypeOf(const_float),
@TypeOf(var_int),
@TypeOf(var_float),
});
}
核心知识点总结
1. comptime_int 是什么?
它不是 int,也不是 long。它是一个数学意义上的纯数字。
在编译阶段,12345 只是一个抽象的概念。直到你把它赋值给 u32,它才变成了占用 4 个字节的二进制数据。
2. 为什么 var 需要显式类型?
var 意味着“请在栈上给我留块地”。
如果编译器只知道初始值是 comptime_int(可以是无限大),它就不知道该留多大的地(1字节?8字节?)。所以,程序员必须明确告知:“我要 32 位 (u32) 的空间”。
3. const 的特权
对于 const,编译器通常不需要为它分配内存地址。它经常被编译器直接内联 (Inline) 到指令中,或者在常量折叠优化中消失。因此,它不需要固定的大小限制。
后续预告:既然我们知道了 comptime 的基本概念,那么我们能不能写一段代码,让它只在编译的时候运行,而在程序启动前就已经算好结果了呢?下一篇我们将学习 comptime 关键字。