Zig 变量声明
在 Zig 中,变量声明是编程的基础。本章将详细介绍如何在 Zig 中声明和使用变量。
变量声明基础
const 声明(常量)
const 用于声明不可变的值:
zig
const std = @import("std");
pub fn main() void {
const name = "张三";
const age = 25;
const pi = 3.14159;
std.debug.print("姓名: {s}, 年龄: {}, π ≈ {d:.2}\n", .{ name, age, pi });
}特点:
- 值在编译时或运行时确定后不能修改
- 必须在声明时初始化
- 可以在编译时计算
var 声明(变量)
var 用于声明可变的值:
zig
const std = @import("std");
pub fn main() void {
var counter = 0;
var message = "开始计数";
std.debug.print("{s}: {}\n", .{ message, counter });
counter = 10;
message = "计数更新";
std.debug.print("{s}: {}\n", .{ message, counter });
}特点:
- 值可以在运行时修改
- 必须在声明时初始化
- 类型一旦确定就不能改变
类型推断与显式类型
类型推断
Zig 可以自动推断变量类型:
zig
const std = @import("std");
pub fn main() void {
// 类型推断
const number = 42; // 推断为 comptime_int
const float_num = 3.14; // 推断为 comptime_float
const text = "Hello"; // 推断为 *const [5:0]u8
const flag = true; // 推断为 bool
std.debug.print("数字: {}, 浮点: {d:.2}, 文本: {s}, 布尔: {}\n",
.{ number, float_num, text, flag });
}显式类型声明
你也可以显式指定类型:
zig
const std = @import("std");
pub fn main() void {
// 显式类型声明
const small_number: i8 = 42;
const big_number: i64 = 1000000;
const precise_float: f64 = 3.141592653589793;
const single_char: u8 = 'A';
std.debug.print("小整数: {}, 大整数: {}, 精确浮点: {d:.10}, 字符: {c}\n",
.{ small_number, big_number, precise_float, single_char });
}变量作用域
块作用域
变量的作用域由代码块决定:
zig
const std = @import("std");
pub fn main() void {
const global_var = "全局变量";
{
const local_var = "局部变量";
std.debug.print("内部块: {s}, {s}\n", .{ global_var, local_var });
}
// local_var 在这里不可访问
std.debug.print("外部块: {s}\n", .{global_var});
}变量遮蔽
内层作用域可以声明同名变量,遮蔽外层变量:
zig
const std = @import("std");
pub fn main() void {
const value = 10;
std.debug.print("外层 value: {}\n", .{value});
{
const value = 20; // 遮蔽外层的 value
std.debug.print("内层 value: {}\n", .{value});
}
std.debug.print("外层 value: {}\n", .{value}); // 仍然是 10
}未定义值
undefined
undefined 表示未初始化的值:
zig
const std = @import("std");
pub fn main() void {
var x: i32 = undefined; // 声明但不初始化
x = 42; // 后续赋值
std.debug.print("x 的值: {}\n", .{x});
// 注意:使用 undefined 的值是未定义行为
// var y: i32 = undefined;
// std.debug.print("{}\n", .{y}); // 危险!
}注意事项:
- 使用
undefined值会导致未定义行为 - 主要用于性能优化,跳过初始化
- 必须在使用前赋值
编译时变量
comptime 变量
comptime 变量在编译时计算:
zig
const std = @import("std");
pub fn main() void {
comptime var compile_time_counter = 0;
// 编译时循环
comptime {
var i = 0;
while (i < 5) : (i += 1) {
compile_time_counter += 1;
}
}
std.debug.print("编译时计算的结果: {}\n", .{compile_time_counter});
}编译时常量
zig
const std = @import("std");
// 编译时常量
const BUFFER_SIZE = 1024;
const VERSION = "1.0.0";
const DEBUG_MODE = true;
pub fn main() void {
std.debug.print("缓冲区大小: {}\n", .{BUFFER_SIZE});
std.debug.print("版本: {s}\n", .{VERSION});
std.debug.print("调试模式: {}\n", .{DEBUG_MODE});
}数组和切片变量
数组变量
zig
const std = @import("std");
pub fn main() void {
// 固定大小数组
var numbers = [_]i32{ 1, 2, 3, 4, 5 };
const fruits = [_][]const u8{ "苹果", "香蕉", "橙子" };
// 修改数组元素
numbers[0] = 10;
std.debug.print("数字: ");
for (numbers) |num| {
std.debug.print("{} ", .{num});
}
std.debug.print("\n");
std.debug.print("水果: ");
for (fruits) |fruit| {
std.debug.print("{s} ", .{fruit});
}
std.debug.print("\n");
}切片变量
zig
const std = @import("std");
pub fn main() void {
var array = [_]i32{ 1, 2, 3, 4, 5 };
// 切片是对数组的引用
var slice: []i32 = array[1..4]; // 包含索引 1, 2, 3
const const_slice: []const i32 = array[0..];
// 修改切片会影响原数组
slice[0] = 100;
std.debug.print("原数组: ");
for (array) |num| {
std.debug.print("{} ", .{num});
}
std.debug.print("\n");
std.debug.print("切片: ");
for (slice) |num| {
std.debug.print("{} ", .{num});
}
std.debug.print("\n");
}结构体变量
zig
const std = @import("std");
const Person = struct {
name: []const u8,
age: u32,
height: f32,
};
pub fn main() void {
// 结构体变量
var person = Person{
.name = "李四",
.age = 30,
.height = 175.5,
};
std.debug.print("姓名: {s}, 年龄: {}, 身高: {d:.1}cm\n",
.{ person.name, person.age, person.height });
// 修改结构体字段
person.age = 31;
person.height = 176.0;
std.debug.print("更新后 - 姓名: {s}, 年龄: {}, 身高: {d:.1}cm\n",
.{ person.name, person.age, person.height });
}可选类型变量
zig
const std = @import("std");
pub fn main() void {
// 可选类型变量
var maybe_number: ?i32 = null;
var maybe_name: ?[]const u8 = "王五";
std.debug.print("maybe_number: ");
if (maybe_number) |num| {
std.debug.print("{}\n", .{num});
} else {
std.debug.print("null\n", .{});
}
std.debug.print("maybe_name: ");
if (maybe_name) |name| {
std.debug.print("{s}\n", .{name});
} else {
std.debug.print("null\n", .{});
}
// 赋值
maybe_number = 42;
maybe_name = null;
std.debug.print("赋值后:\n");
std.debug.print("maybe_number: {?}\n", .{maybe_number});
std.debug.print("maybe_name: {?s}\n", .{maybe_name});
}变量初始化模式
默认初始化
zig
const std = @import("std");
pub fn main() void {
// 基本类型的默认值
var int_var: i32 = 0;
var float_var: f64 = 0.0;
var bool_var: bool = false;
var optional_var: ?i32 = null;
std.debug.print("整数: {}, 浮点: {d:.1}, 布尔: {}, 可选: {?}\n",
.{ int_var, float_var, bool_var, optional_var });
}批量初始化
zig
const std = @import("std");
pub fn main() void {
// 数组批量初始化
var zeros = [_]i32{0} ** 5; // 5个0
var ones = [_]i32{1} ** 3; // 3个1
std.debug.print("zeros: ");
for (zeros) |val| {
std.debug.print("{} ", .{val});
}
std.debug.print("\n");
std.debug.print("ones: ");
for (ones) |val| {
std.debug.print("{} ", .{val});
}
std.debug.print("\n");
}常见错误和注意事项
错误 1:未初始化变量
zig
// ❌ 错误:变量必须初始化
// var x: i32; // 编译错误
// ✅ 正确:显式初始化
var x: i32 = 0;
// 或者
var y: i32 = undefined; // 明确表示未初始化错误 2:修改常量
zig
// ❌ 错误:不能修改常量
// const x = 10;
// x = 20; // 编译错误
// ✅ 正确:使用变量
var x = 10;
x = 20; // OK错误 3:类型不匹配
zig
// ❌ 错误:类型不匹配
// var x: i32 = 3.14; // 编译错误
// ✅ 正确:类型匹配
var x: f32 = 3.14;
// 或者类型转换
var y: i32 = @intFromFloat(3.14);最佳实践
1. 优先使用 const
zig
// ✅ 好的做法:默认使用 const
const name = "用户名";
const config = loadConfig();
// 只有需要修改时才使用 var
var counter = 0;
var current_state = State.Initial;2. 明确的类型声明
zig
// ✅ 当类型不明显时,显式声明
const buffer: [1024]u8 = undefined;
const timeout: u64 = 5000; // 毫秒
// 类型明显时可以省略
const message = "Hello"; // 明显是字符串
const count = 42; // 明显是整数3. 有意义的变量名
zig
// ✅ 好的变量名
const user_count = 100;
const max_retry_attempts = 3;
const is_debug_enabled = true;
// ❌ 避免的变量名
const x = 100;
const n = 3;
const flag = true;4. 合理的作用域
zig
pub fn processData() void {
const input_file = "data.txt";
// 限制变量作用域
{
const temp_buffer = allocateBuffer();
defer freeBuffer(temp_buffer);
// 使用 temp_buffer
}
// temp_buffer 在这里已经不可访问
}实践练习
练习 1:基本变量操作
zig
const std = @import("std");
pub fn main() void {
// TODO: 声明一个整数变量,初始值为 10
// TODO: 声明一个字符串常量,值为你的名字
// TODO: 声明一个浮点数变量,初始值为 0.0
// TODO: 修改整数变量的值为 20
// TODO: 修改浮点数变量的值为 3.14
// TODO: 打印所有变量的值
}练习 2:数组和结构体
zig
const std = @import("std");
const Student = struct {
name: []const u8,
age: u8,
grades: [3]f32,
};
pub fn main() void {
// TODO: 创建一个学生变量
// TODO: 修改学生的年龄
// TODO: 修改学生的第一个成绩
// TODO: 计算平均成绩
// TODO: 打印学生信息和平均成绩
}总结
本章介绍了 Zig 中变量声明的各个方面:
- ✅
const和var的区别和使用 - ✅ 类型推断与显式类型声明
- ✅ 变量作用域和遮蔽
- ✅ 特殊值
undefined的使用 - ✅ 编译时变量和常量
- ✅ 复合类型变量的声明
- ✅ 常见错误和最佳实践
掌握变量声明是 Zig 编程的基础,为后续学习数据类型和更复杂的概念打下坚实基础。在下一章中,我们将深入学习 Zig 的数据类型系统。