Skip to content

Zig 流程控制

流程控制是编程中的核心概念,本章将详细介绍 Zig 中的各种流程控制语句。

if 语句

基本 if 语句

if 语句用于条件判断:

zig
const std = @import("std");

pub fn main() void {
    const age = 18;
    
    if (age >= 18) {
        std.debug.print("你已经成年了\n", .{});
    }
    
    const temperature = 25;
    if (temperature > 30) {
        std.debug.print("天气很热\n", .{});
    } else {
        std.debug.print("天气还不错\n", .{});
    }
}

if-else if 链

zig
const std = @import("std");

pub fn main() void {
    const score = 85;
    
    if (score >= 90) {
        std.debug.print("优秀\n", .{});
    } else if (score >= 80) {
        std.debug.print("良好\n", .{});
    } else if (score >= 70) {
        std.debug.print("中等\n", .{});
    } else if (score >= 60) {
        std.debug.print("及格\n", .{});
    } else {
        std.debug.print("不及格\n", .{});
    }
}

if 表达式

在 Zig 中,if 是表达式,可以返回值:

zig
const std = @import("std");

pub fn main() void {
    const x = 10;
    const y = 20;
    
    // if 表达式
    const max = if (x > y) x else y;
    std.debug.print("最大值: {}\n", .{max});
    
    // 复杂的 if 表达式
    const status = if (x > 0) 
        "正数" 
    else if (x < 0) 
        "负数" 
    else 
        "零";
    
    std.debug.print("数字状态: {s}\n", .{status});
}

可选值的 if 处理

if 可以用来处理可选值:

zig
const std = @import("std");

pub fn main() void {
    var maybe_number: ?i32 = 42;
    
    // 解包可选值
    if (maybe_number) |number| {
        std.debug.print("数字是: {}\n", .{number});
    } else {
        std.debug.print("没有数字\n", .{});
    }
    
    // 修改为 null 并再次测试
    maybe_number = null;
    if (maybe_number) |number| {
        std.debug.print("数字是: {}\n", .{number});
    } else {
        std.debug.print("现在没有数字\n", .{});
    }
}

错误联合类型的 if 处理

zig
const std = @import("std");

const ParseError = error{
    InvalidFormat,
    OutOfRange,
};

fn parseNumber(input: []const u8) ParseError!i32 {
    if (std.mem.eql(u8, input, "42")) {
        return 42;
    } else if (std.mem.eql(u8, input, "100")) {
        return 100;
    } else if (std.mem.eql(u8, input, "999")) {
        return ParseError.OutOfRange;
    } else {
        return ParseError.InvalidFormat;
    }
}

pub fn main() void {
    const inputs = [_][]const u8{ "42", "100", "999", "abc" };
    
    for (inputs) |input| {
        if (parseNumber(input)) |number| {
            std.debug.print("解析成功: {} -> {}\n", .{ input, number });
        } else |err| {
            std.debug.print("解析失败: {} -> {}\n", .{ input, err });
        }
    }
}

switch 语句

基本 switch 语句

switch 语句用于多分支选择:

zig
const std = @import("std");

pub fn main() void {
    const day = 3;
    
    switch (day) {
        1 => std.debug.print("星期一\n", .{}),
        2 => std.debug.print("星期二\n", .{}),
        3 => std.debug.print("星期三\n", .{}),
        4 => std.debug.print("星期四\n", .{}),
        5 => std.debug.print("星期五\n", .{}),
        6, 7 => std.debug.print("周末\n", .{}),
        else => std.debug.print("无效的日期\n", .{}),
    }
}

switch 表达式

switch 也可以作为表达式使用:

zig
const std = @import("std");

pub fn main() void {
    const grade = 'B';
    
    const score = switch (grade) {
        'A' => 90,
        'B' => 80,
        'C' => 70,
        'D' => 60,
        'F' => 0,
        else => -1,
    };
    
    std.debug.print("等级 {} 对应分数: {}\n", .{ grade, score });
}

范围匹配

switch 支持范围匹配:

zig
const std = @import("std");

pub fn main() void {
    const number = 75;
    
    const category = switch (number) {
        0...59 => "不及格",
        60...69 => "及格",
        70...79 => "中等",
        80...89 => "良好",
        90...100 => "优秀",
        else => "无效分数",
    };
    
    std.debug.print("分数 {} 属于: {s}\n", .{ number, category });
}

枚举的 switch

switch 与枚举配合使用非常强大:

zig
const std = @import("std");

const Color = enum {
    Red,
    Green,
    Blue,
    Yellow,
};

fn getColorName(color: Color) []const u8 {
    return switch (color) {
        .Red => "红色",
        .Green => "绿色",
        .Blue => "蓝色",
        .Yellow => "黄色",
    };
}

pub fn main() void {
    const colors = [_]Color{ .Red, .Green, .Blue, .Yellow };
    
    for (colors) |color| {
        std.debug.print("颜色: {s}\n", .{getColorName(color)});
    }
}

联合类型的 switch

switch 可以处理联合类型:

zig
const std = @import("std");

const Value = union(enum) {
    Integer: i32,
    Float: f64,
    String: []const u8,
    Boolean: bool,
};

fn processValue(value: Value) void {
    switch (value) {
        .Integer => |int| std.debug.print("整数: {}\n", .{int}),
        .Float => |float| std.debug.print("浮点数: {d:.2}\n", .{float}),
        .String => |string| std.debug.print("字符串: {s}\n", .{string}),
        .Boolean => |boolean| std.debug.print("布尔值: {}\n", .{boolean}),
    }
}

pub fn main() void {
    const values = [_]Value{
        Value{ .Integer = 42 },
        Value{ .Float = 3.14 },
        Value{ .String = "Hello" },
        Value{ .Boolean = true },
    };
    
    for (values) |value| {
        processValue(value);
    }
}

复杂的 switch 模式

zig
const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

fn analyzePoint(point: Point) []const u8 {
    return switch (point) {
        .{ .x = 0, .y = 0 } => "原点",
        .{ .x = 0, .y = _ } => "Y轴上",
        .{ .x = _, .y = 0 } => "X轴上",
        .{ .x = var x, .y = var y } => blk: {
            if (x == y) {
                break :blk "对角线上";
            } else if (x > 0 and y > 0) {
                break :blk "第一象限";
            } else if (x < 0 and y > 0) {
                break :blk "第二象限";
            } else if (x < 0 and y < 0) {
                break :blk "第三象限";
            } else {
                break :blk "第四象限";
            }
        },
    };
}

pub fn main() void {
    const points = [_]Point{
        .{ .x = 0, .y = 0 },
        .{ .x = 0, .y = 5 },
        .{ .x = 3, .y = 0 },
        .{ .x = 2, .y = 2 },
        .{ .x = 3, .y = 4 },
        .{ .x = -2, .y = 3 },
        .{ .x = -1, .y = -1 },
        .{ .x = 2, .y = -3 },
    };
    
    for (points) |point| {
        std.debug.print("点 ({}, {}) 位于: {s}\n", 
                        .{ point.x, point.y, analyzePoint(point) });
    }
}

defer 语句

基本 defer

defer 语句在当前作用域结束时执行:

zig
const std = @import("std");

pub fn main() void {
    std.debug.print("开始执行\n", .{});
    
    defer std.debug.print("这会在函数结束时执行\n", .{});
    
    std.debug.print("中间执行\n", .{});
    
    // 函数结束时会执行 defer 语句
}

多个 defer 语句

多个 defer 语句按照后进先出(LIFO)的顺序执行:

zig
const std = @import("std");

pub fn main() void {
    std.debug.print("开始\n", .{});
    
    defer std.debug.print("第一个 defer\n", .{});
    defer std.debug.print("第二个 defer\n", .{});
    defer std.debug.print("第三个 defer\n", .{});
    
    std.debug.print("中间\n", .{});
    
    // 执行顺序:第三个 defer -> 第二个 defer -> 第一个 defer
}

defer 的实际应用

defer 常用于资源清理:

zig
const std = @import("std");

fn processFile(filename: []const u8) !void {
    std.debug.print("打开文件: {s}\n", .{filename});
    
    // 模拟文件操作
    defer std.debug.print("关闭文件: {s}\n", .{filename});
    
    std.debug.print("处理文件内容\n", .{});
    
    // 即使发生错误,defer 也会执行
    if (std.mem.eql(u8, filename, "error.txt")) {
        return error.FileError;
    }
    
    std.debug.print("文件处理完成\n", .{});
}

pub fn main() void {
    processFile("normal.txt") catch |err| {
        std.debug.print("处理文件时出错: {}\n", .{err});
    };
    
    std.debug.print("---\n", .{});
    
    processFile("error.txt") catch |err| {
        std.debug.print("处理文件时出错: {}\n", .{err});
    };
}

errdefer 语句

基本 errdefer

errdefer 只在函数返回错误时执行:

zig
const std = @import("std");

const ProcessError = error{
    InitializationFailed,
    ProcessingFailed,
};

fn processData(should_fail: bool) ProcessError!void {
    std.debug.print("开始初始化\n", .{});
    
    errdefer std.debug.print("初始化失败,执行清理\n", .{});
    
    if (should_fail) {
        return ProcessError.InitializationFailed;
    }
    
    std.debug.print("初始化成功\n", .{});
    std.debug.print("处理数据\n", .{});
}

pub fn main() void {
    std.debug.print("=== 成功情况 ===\n", .{});
    processData(false) catch |err| {
        std.debug.print("错误: {}\n", .{err});
    };
    
    std.debug.print("\n=== 失败情况 ===\n", .{});
    processData(true) catch |err| {
        std.debug.print("错误: {}\n", .{err});
    };
}

errdefer 与 defer 的组合

zig
const std = @import("std");

const ResourceError = error{
    AllocationFailed,
    InitializationFailed,
};

fn allocateAndInitialize(should_fail_alloc: bool, should_fail_init: bool) ResourceError!void {
    std.debug.print("开始分配资源\n", .{});
    
    if (should_fail_alloc) {
        return ResourceError.AllocationFailed;
    }
    
    // 资源分配成功
    defer std.debug.print("释放资源\n", .{});
    errdefer std.debug.print("分配成功但初始化失败,释放资源\n", .{});
    
    std.debug.print("开始初始化\n", .{});
    
    if (should_fail_init) {
        return ResourceError.InitializationFailed;
    }
    
    std.debug.print("初始化成功\n", .{});
}

pub fn main() void {
    std.debug.print("=== 分配失败 ===\n", .{});
    allocateAndInitialize(true, false) catch |err| {
        std.debug.print("错误: {}\n", .{err});
    };
    
    std.debug.print("\n=== 初始化失败 ===\n", .{});
    allocateAndInitialize(false, true) catch |err| {
        std.debug.print("错误: {}\n", .{err});
    };
    
    std.debug.print("\n=== 全部成功 ===\n", .{});
    allocateAndInitialize(false, false) catch |err| {
        std.debug.print("错误: {}\n", .{err});
    };
}

块表达式

基本块表达式

块可以作为表达式使用:

zig
const std = @import("std");

pub fn main() void {
    const result = blk: {
        const x = 10;
        const y = 20;
        break :blk x + y;
    };
    
    std.debug.print("块表达式结果: {}\n", .{result});
}

复杂的块表达式

zig
const std = @import("std");

fn calculateGrade(score: i32) []const u8 {
    return blk: {
        if (score >= 90) {
            break :blk "A";
        } else if (score >= 80) {
            break :blk "B";
        } else if (score >= 70) {
            break :blk "C";
        } else if (score >= 60) {
            break :blk "D";
        } else {
            break :blk "F";
        }
    };
}

pub fn main() void {
    const scores = [_]i32{ 95, 85, 75, 65, 55 };
    
    for (scores) |score| {
        const grade = calculateGrade(score);
        std.debug.print("分数 {} 对应等级: {s}\n", .{ score, grade });
    }
}

条件编译

comptime if

使用 comptime if 进行条件编译:

zig
const std = @import("std");
const builtin = @import("builtin");

pub fn main() void {
    comptime {
        if (builtin.os.tag == .windows) {
            std.debug.print("编译目标是 Windows\n", .{});
        } else if (builtin.os.tag == .linux) {
            std.debug.print("编译目标是 Linux\n", .{});
        } else if (builtin.os.tag == .macos) {
            std.debug.print("编译目标是 macOS\n", .{});
        } else {
            std.debug.print("编译目标是其他系统\n", .{});
        }
    }
    
    const is_debug = comptime builtin.mode == .Debug;
    
    if (comptime is_debug) {
        std.debug.print("这是调试版本\n", .{});
    } else {
        std.debug.print("这是发布版本\n", .{});
    }
}

编译时 switch

zig
const std = @import("std");

fn getOptimizationLevel(comptime mode: std.builtin.Mode) []const u8 {
    return comptime switch (mode) {
        .Debug => "无优化",
        .ReleaseSafe => "安全优化",
        .ReleaseFast => "速度优化",
        .ReleaseSmall => "大小优化",
    };
}

pub fn main() void {
    const optimization = comptime getOptimizationLevel(@import("builtin").mode);
    std.debug.print("当前优化级别: {s}\n", .{optimization});
}

流程控制的最佳实践

1. 保持条件简洁

zig
const std = @import("std");

pub fn main() void {
    const user_age = 25;
    const has_license = true;
    const has_insurance = true;
    
    // ✅ 好的做法:条件清晰
    const can_drive = user_age >= 18 and has_license and has_insurance;
    
    if (can_drive) {
        std.debug.print("可以开车\n", .{});
    } else {
        std.debug.print("不能开车\n", .{});
    }
}

2. 优先使用 switch 而不是长的 if-else 链

zig
const std = @import("std");

const Status = enum {
    Pending,
    Processing,
    Completed,
    Failed,
};

pub fn main() void {
    const status = Status.Processing;
    
    // ✅ 好的做法:使用 switch
    const message = switch (status) {
        .Pending => "等待中",
        .Processing => "处理中",
        .Completed => "已完成",
        .Failed => "失败",
    };
    
    std.debug.print("状态: {s}\n", .{message});
}

3. 合理使用 defer 进行资源管理

zig
const std = @import("std");

fn processWithCleanup() !void {
    std.debug.print("获取资源\n", .{});
    
    // ✅ 好的做法:立即设置清理代码
    defer std.debug.print("释放资源\n", .{});
    
    std.debug.print("使用资源\n", .{});
    
    // 无论函数如何退出,资源都会被释放
}

pub fn main() void {
    processWithCleanup() catch {};
}

实践练习

练习 1:成绩管理系统

zig
const std = @import("std");

const Student = struct {
    name: []const u8,
    score: i32,
};

pub fn main() void {
    const students = [_]Student{
        .{ .name = "张三", .score = 85 },
        .{ .name = "李四", .score = 92 },
        .{ .name = "王五", .score = 78 },
        .{ .name = "赵六", .score = 65 },
    };
    
    // TODO: 使用流程控制语句完成以下任务:
    // 1. 为每个学生分配等级 (A: 90+, B: 80-89, C: 70-79, D: 60-69, F: <60)
    // 2. 统计各等级的人数
    // 3. 找出最高分和最低分的学生
}

练习 2:简单计算器

zig
const std = @import("std");

const Operation = enum {
    Add,
    Subtract,
    Multiply,
    Divide,
};

pub fn main() void {
    // TODO: 实现一个简单的计算器
    // 1. 使用 switch 处理不同的运算操作
    // 2. 处理除零错误
    // 3. 使用 defer 进行资源清理
}

练习 3:状态机

zig
const std = @import("std");

const State = enum {
    Idle,
    Running,
    Paused,
    Stopped,
};

const Event = enum {
    Start,
    Pause,
    Resume,
    Stop,
};

pub fn main() void {
    // TODO: 实现一个简单的状态机
    // 1. 根据当前状态和事件,决定下一个状态
    // 2. 使用 switch 处理状态转换
    // 3. 处理无效的状态转换
}

总结

本章详细介绍了 Zig 的流程控制语句:

  • if 语句:条件判断,支持表达式形式
  • switch 语句:多分支选择,功能强大
  • defererrdefer:资源管理和清理
  • ✅ 块表达式:将代码块作为表达式使用
  • ✅ 条件编译:编译时流程控制
  • ✅ 最佳实践和实际应用

掌握流程控制是编写复杂程序逻辑的基础。在下一章中,我们将学习 Zig 的错误处理机制。

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