Skip to content

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 中变量声明的各个方面:

  • constvar 的区别和使用
  • ✅ 类型推断与显式类型声明
  • ✅ 变量作用域和遮蔽
  • ✅ 特殊值 undefined 的使用
  • ✅ 编译时变量和常量
  • ✅ 复合类型变量的声明
  • ✅ 常见错误和最佳实践

掌握变量声明是 Zig 编程的基础,为后续学习数据类型和更复杂的概念打下坚实基础。在下一章中,我们将深入学习 Zig 的数据类型系统。

本站内容仅供学习和研究使用。