🔤 第02课:变量与类型

📋 本课目录 变量声明 整数类型 浮点类型 布尔与可选类型 字符串与字节 数组 向量类型 类型反射 编译期类型 课后练习 🏆 成就解锁

变量声明

Zig提供三种变量声明方式,每种都有明确的语义:

const std = @import("std");

pub fn main() !void {
    // const: 不可变绑定——声明后不可修改
    const pi: f64 = 3.14159265358979;

    // var: 可变绑定——可以重新赋值
    var counter: u32 = 0;
    counter += 1;

    // 空白标识符:丢弃值(必须使用,Zig不允许未使用变量)
    _ = pi;  // 显式忽略pi的值

    // 类型推断:Zig可以自动推断类型
    const x = 42;           // 推断为 comptime_int
    const y: i64 = 42;      // 显式类型 i64
    const z: usize = 42;    // 显式类型 usize

    std.debug.print("counter = {}, x = {}\n", .{counter, x});
}

变量命名规则

const snake_case_var = 10;   // 变量
const MyType = u32;         // 类型别名
var unused_x_ = 5;          // 带未使用后缀
💡 Zig的const vs C的const:Zig的const是真正的不可变——编译器保证值不会改变。C的const只是"不应该改变",可以通过指针绕过。Zig中没有这种后门。

整数类型

固定宽度整数

Zig提供完整的有符号和无符号整数类型:

类型范围大小
i8-128 ~ 1271字节
u80 ~ 2551字节
i16-32768 ~ 327672字节
u160 ~ 655352字节
i32-2³¹ ~ 2³¹-14字节
u320 ~ 2³²-14字节
i64-2⁶³ ~ 2⁶³-18字节
u640 ~ 2⁶⁴-18字节
i128-2¹²⁷ ~ 2¹²⁷-116字节
u1280 ~ 2¹²⁸-116字节
isize指针大小有符号平台相关
usize指针大小无符号平台相关

任意位宽整数

Zig支持任意位宽的整数类型:

const a: i7 = -64;      // 7位有符号整数
const b: u23 = 8388607;  // 23位无符号整数
const c: i1 = -1;        // 1位有符号整数(-1或0)
const d: u0 = 0;         // 0位整数(只能为0,编译期常量)

整数运算与安全

const std = @import("std");

pub fn main() !void {
    var x: u8 = 200;
    var y: u8 = 100;

    // 标准运算——Debug模式溢出会panic
    const sum = x + y;  // Debug: panic! ReleaseFast: 回绕

    // 显式回绕运算(所有模式都回绕)
    const wrapped = std.math.add(u8, x, y) catch 0;

    // 饱和运算——超过范围取边界值
    const sat_add = std.math.clamp(@as(u16, x) + y, 0, 255);

    // 使用溢出运算符
    const ov = @addWithOverflow(x, y);
    if (ov[1] != 0) {
        std.debug.print("溢出! 结果={}\n", .{ov[0]});
    }

    // 宽化运算——自动提升到更大类型
    const wide: u16 = @as(u16, x) + y;
    std.debug.print("安全求和: {}\n", .{wide});

    _ = sum; _ = wrapped; _ = sat_add;
}

整数类型转换

const a: i32 = 42;

// 宽化转换(小→大,安全)
const b: i64 = @intCast(a);

// 窄化转换(大→小,可能截断)
const c: i8 = @intCast(a); // Debug模式下超出范围会panic

// 有符号↔无符号
const d: u32 = @intCast(a);  // 负数会panic
const e: i32 = @intCast(d);  // 超出i32范围会panic

// 位重解释(不改变底层位模式)
const f: u32 = @bitCast(a);

// 指针→整数
const ptr_val = @intFromPtr(&a);

// 浮点→整数(截断小数)
const g: i32 = @intFromFloat(3.7); // g = 3

浮点类型

const std = @import("std");

pub fn main() !void {
    const f16_val: f16 = 3.14;   // 半精度(16位)
    const f32_val: f32 = 3.14159; // 单精度(32位)
    const f64_val: f64 = 3.14159265358979; // 双精度(64位)
    const f80_val: f80 = 3.14159265358979; // 扩展精度(80位)
    const f128_val: f128 = 3.141592653589793238; // 四精度(128位)

    // 浮点运算
    const result = f32_val * 2.0 + 1.0;

    // 数学函数
    const sqrt_val = std.math.sqrt(f64_val);
    const sin_val = std.math.sin(f64_val);
    const abs_val = std.math.fabs(f64_val);

    // 浮点→整数
    const int_val: i32 = @intFromFloat(f64_val);

    // 整数→浮点
    const float_from_int: f64 = @floatFromInt(@as(i32, 42));

    std.debug.print("sqrt(π) = {d:.6}\n", .{sqrt_val});
    std.debug.print("int from float = {}\n", .{int_val});

    _ = f16_val; _ = f80_val; _ = f128_val; _ = result;
    _ = sin_val; _ = abs_val; _ = float_from_int;
}

布尔与可选类型

布尔类型

const std = @import("std");

pub fn main() !void {
    const is_true: bool = true;
    const is_false: bool = false;

    // 布尔运算
    const and_result = is_true and is_false;   // 逻辑与
    const or_result = is_true or is_false;     // 逻辑或
    const not_result = !is_true;               // 逻辑非

    // 布尔→整数
    const bool_to_int: u1 = @intFromBool(is_true);

    std.debug.print("true AND false = {}\n", .{and_result});
    std.debug.print("bool as int = {}\n", .{bool_to_int});

    _ = or_result; _ = not_result;
}

可选类型 ?T

Zig用?T表示可能为null的值——这是类型系统级别的空安全:

const std = @import("std");

pub fn main() !void {
    // 可选整数——可以是整数或null
    var maybe_num: ?i32 = 42;
    maybe_num = null;  // 可以赋值为null

    // 检查可选值
    if (maybe_num) |num| {
        std.debug.print("值存在: {}\n", .{num});
    } else {
        std.debug.print("值为null\n", .{});
    }

    // orelse提供默认值
    const value = maybe_num orelse 0;

    // 可选指针
    var x: i32 = 100;
    var ptr: ?*i32 = &x;
    ptr = null;

    // 可选解包——.?操作符
    maybe_num = 99;
    const unwrapped = maybe_num.?;  // 如果是null则panic

    std.debug.print("value={}, unwrapped={}\n", .{value, unwrapped});
    _ = ptr;
}

字符串与字节

Zig没有专门的字符串类型——字符串是[]const u8(u8常量切片):

const std = @import("std");

pub fn main() !void {
    // 字符串字面量——编译期存在的[]const u8
    const greeting = "Hello, Zig!";

    // 字符串长度(不包括末尾的0)
    std.debug.print("长度: {}\n", .{greeting.len});

    // 单字节访问
    std.debug.print("第一个字节: 0x{x:02}\n", .{greeting[0]});

    // 遍历字节
    for (greeting) |byte| {
        std.debug.print("{c}", .{byte});
    }
    std.debug.print("\n", .{});

    // 多行字符串
    const multi_line =
        \\第一行
        \\第二行
        \\第三行
    ;

    // 字符串连接(编译期)
    const combined = "Hello" ++ " " ++ "World";

    // 字符串格式化
    const name = "Zig";
    const version = 0.13;
    std.debug.print("{s} v{d:.2}\n", .{ name, version });

    _ = multi_line; _ = combined;
}

UTF-8处理

const std = @import("std");
const unicode = std.unicode;

pub fn main() !void {
    const chinese = "你好世界";

    // 字节长度 vs Unicode码点数
    std.debug.print("字节长度: {}\n", .{chinese.len});  // 12(每个汉字3字节)

    // 迭代UTF-8码点
    var iter = unicode.Utf8Iterator{ .bytes = chinese, .i = 0 };
    var count: usize = 0;
    while (iter.nextCodepoint()) |cp| {
        count += 1;
        std.debug.print("码点: U+{X:0>4}\n", .{cp});
    }
    std.debug.print("Unicode字符数: {}\n", .{count});  // 4
}

数组

固定数组

const std = @import("std");

pub fn main() !void {
    // 固定大小数组
    const arr = [5]i32{ 1, 2, 3, 4, 5 };

    // 编译期推断大小
    const inferred = .{ 10, 20, 30 };

    // 重复值初始化
    const zeros = [100]u8{0} ** 1;

    // 数组到切片
    const slice: []const i32 = &arr;

    // 遍历数组
    for (arr, 0..) |value, index| {
        std.debug.print("arr[{}] = {}\n", .{ index, value });
    }

    // 多维数组
    const matrix = [2][3]f32{
        .{ 1.0, 2.0, 3.0 },
        .{ 4.0, 5.0, 6.0 },
    };

    std.debug.print("matrix[1][2] = {d:.1}\n", .{matrix[1][2]});

    // 数组拼接(编译期)
    const a = [3]u8{ 1, 2, 3 };
    const b = [2]u8{ 4, 5 };
    const c = a ++ b;  // [5]u8{ 1, 2, 3, 4, 5 }

    _ = inferred; _ = zeros; _ = slice; _ = c;
}

切片(Slice)

const std = @import("std");

pub fn main() !void {
    var arr = [5]i32{ 10, 20, 30, 40, 50 };

    // 切片语法
    const full = arr[0..];       // 全部元素
    const part = arr[1..3];     // arr[1], arr[2]
    const from = arr[2..];      // 从索引2开始

    // 切片属性
    std.debug.print("len={}, ptr={*}\n", .{ full.len, full.ptr });

    // 运行时切片(需要运行时索引)
    var start: usize = 1;
    const runtime_slice = arr[start..];

    // 切片重新赋值
    var mutable_slice = arr[0..];
    mutable_slice[0] = 99;

    _ = part; _ = from; _ = runtime_slice;
    std.debug.print("arr[0] = {}\n", .{arr[0]});
}

向量类型

Zig内置SIMD向量支持,无需外部库:

const std = @import("std");

pub fn main() !void {
    // 4个f32的SIMD向量
    const v1 = @Vector(4, f32){ 1.0, 2.0, 3.0, 4.0 };
    const v2 = @Vector(4, f32){ 5.0, 6.0, 7.0, 8.0 };

    // 向量运算——一条指令同时处理4个值
    const sum = v1 + v2;     // [6,8,10,12]
    const product = v1 * v2; // [5,12,21,32]

    // 向量与标量运算
    const scaled = v1 * @splat(2.0);  // [2,4,6,8]

    // 向量比较
    const mask = v1 > @splat(2.0); // [0,0,1,1]

    // 向量→数组
    const arr: [4]f32 = sum;
    std.debug.print("sum = {d:.1}\n", .{arr});

    // 数组→向量
    const from_arr: @Vector(4, f32) = .{ 1.0, 2.0, 3.0, 4.0 };

    _ = product; _ = scaled; _ = mask; _ = from_arr;
}

类型反射

Zig的@typeInfo提供了完整的编译期类型反射:

const std = @import("std");

pub fn main() !void {
    // 获取类型信息
    const info = @typeInfo(i32);
    switch (info) {
        .Int => |int_info| {
            std.debug.print("i32: signed={}, bits={}\n", .{
                int_info.signedness == .signed,
                int_info.bits,
            });
        },
        else => unreachable,
    }

    // 获取类型大小
    std.debug.print("i32大小: {}字节\n", .{@sizeOf(i32)});
    std.debug.print("f64大小: {}字节\n", .{@sizeOf(f64)});
    std.debug.print("bool大小: {}字节\n", .{@sizeOf(bool)});

    // 类型对齐
    std.debug.print("u64对齐: {}\n", .{@alignOf(u64)});

    // 类型名