复合类型 #

一、数组 #

1.1 数组声明 #

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

pub fn main() void {
    // 完整声明
    const arr1: [5]i32 = [5]i32{ 1, 2, 3, 4, 5 };
    
    // 类型推断
    const arr2 = [_]i32{ 1, 2, 3, 4, 5 };
    
    // 指定大小
    const arr3 = [5]i32{ 1, 2, 3, 4, 5 };
    
    std.debug.print("arr1 len: {}\n", .{arr1.len});
    std.debug.print("arr2 len: {}\n", .{arr2.len});
    std.debug.print("arr3 len: {}\n", .{arr3.len});
}

1.2 数组初始化 #

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

pub fn main() void {
    // 列表初始化
    const arr1 = [_]i32{ 1, 2, 3 };
    
    // 重复初始化
    const arr2 = [_]i32{0} ** 5;
    
    // 部分初始化(其余为零)
    const arr3 = [_]i32{ 1, 2 } ++ [_]i32{0} ** 3;
    
    // 使用 for 循环初始化
    var arr4: [5]i32 = undefined;
    for (&arr4, 0..) |*item, i| {
        item.* = @intCast(i * i);
    }
    
    std.debug.print("arr1: {any}\n", .{arr1});
    std.debug.print("arr2: {any}\n", .{arr2});
    std.debug.print("arr3: {any}\n", .{arr3});
    std.debug.print("arr4: {any}\n", .{arr4});
}

1.3 数组访问 #

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

pub fn main() void {
    const arr = [_]i32{ 10, 20, 30, 40, 50 };
    
    // 索引访问
    std.debug.print("arr[0] = {}\n", .{arr[0]});
    std.debug.print("arr[4] = {}\n", .{arr[4]});
    
    // 边界检查(编译时)
    // std.debug.print("arr[5] = {}\n", .{arr[5]});  // 编译错误
    
    // 运行时边界检查
    var i: usize = 0;
    i = 3;
    std.debug.print("arr[i] = {}\n", .{arr[i]});
}

1.4 数组遍历 #

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

pub fn main() void {
    const arr = [_]i32{ 10, 20, 30, 40, 50 };
    
    // 遍历值
    for (arr) |value| {
        std.debug.print("{} ", .{value});
    }
    std.debug.print("\n", .{});
    
    // 遍历值和索引
    for (arr, 0..) |value, i| {
        std.debug.print("arr[{}] = {}\n", .{ i, value });
    }
    
    // 遍历指针(可修改)
    var mutable_arr = [_]i32{ 1, 2, 3, 4, 5 };
    for (&mutable_arr) |*item| {
        item.* *= 2;
    }
    std.debug.print("modified: {any}\n", .{mutable_arr});
}

1.5 多维数组 #

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

pub fn main() void {
    // 二维数组
    const matrix: [3][3]i32 = [3][3]i32{
        .{ 1, 2, 3 },
        .{ 4, 5, 6 },
        .{ 7, 8, 9 },
    };
    
    std.debug.print("matrix[0][0] = {}\n", .{matrix[0][0]});
    std.debug.print("matrix[1][1] = {}\n", .{matrix[1][1]});
    
    // 遍历二维数组
    for (matrix, 0..) |row, i| {
        for (row, 0..) |val, j| {
            std.debug.print("[{}][{}] = {}  ", .{ i, j, val });
        }
        std.debug.print("\n", .{});
    }
}

二、切片 #

2.1 切片基础 #

切片是对数组或另一个切片的视图:

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

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    
    // 创建切片
    const slice1: []i32 = arr[0..];
    const slice2: []i32 = arr[1..4];
    const slice3: []i32 = arr[0..arr.len];
    
    std.debug.print("slice1 len: {}\n", .{slice1.len});
    std.debug.print("slice2: {any}\n", .{slice2});
    std.debug.print("slice3 len: {}\n", .{slice3.len});
}

2.2 切片操作 #

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

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    var slice: []i32 = arr[0..];
    
    // 修改切片元素
    slice[0] = 100;
    
    // 切片再切片
    const sub_slice = slice[2..5];
    
    std.debug.print("slice: {any}\n", .{slice});
    std.debug.print("sub_slice: {any}\n", .{sub_slice});
}

2.3 切片与指针 #

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

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    
    // 可变切片
    var slice: []i32 = arr[0..];
    
    // 只读切片
    const const_slice: []const i32 = arr[0..];
    
    // 修改可变切片
    slice[0] = 100;
    
    // 不能修改只读切片
    // const_slice[0] = 100;  // 编译错误
    
    std.debug.print("arr: {any}\n", .{arr});
}

2.4 切片指针 #

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

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    
    // 切片指针(包含长度信息)
    const slice_ptr: [*]i32 = &arr;
    
    // 访问元素
    std.debug.print("slice_ptr[0] = {}\n", .{slice_ptr[0]});
    std.debug.print("slice_ptr[1] = {}\n", .{slice_ptr[1]});
    
    // 注意:切片指针没有长度信息,需要手动管理
}

2.5 切片遍历 #

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

pub fn main() void {
    var arr = [_]i32{ 10, 20, 30, 40, 50 };
    var slice: []i32 = arr[1..4];
    
    // 遍历值
    for (slice) |value| {
        std.debug.print("{} ", .{value});
    }
    std.debug.print("\n", .{});
    
    // 遍历并修改
    for (slice) |*item| {
        item.* += 5;
    }
    std.debug.print("modified slice: {any}\n", .{slice});
    std.debug.print("original arr: {any}\n", .{arr});
}

三、元组 #

3.1 元组基础 #

元组是匿名的结构体,可以包含不同类型的元素:

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

pub fn main() void {
    // 创建元组
    const tuple = .{ 1, "hello", 3.14, true };
    
    // 访问元素
    std.debug.print("tuple[0] = {}\n", .{tuple[0]});
    std.debug.print("tuple[1] = {s}\n", .{tuple[1]});
    std.debug.print("tuple[2] = {d}\n", .{tuple[2]});
    std.debug.print("tuple[3] = {}\n", .{tuple[3]});
}

3.2 元组解构 #

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

pub fn main() void {
    const tuple = .{ 10, "Zig", 3.14 };
    
    // 解构
    const a, const b, const c = tuple;
    
    std.debug.print("a = {}\n", .{a});
    std.debug.print("b = {s}\n", .{b});
    std.debug.print("c = {d}\n", .{c});
}

3.3 元组遍历 #

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

pub fn main() void {
    const tuple = .{ 1, "hello", 3.14, true };
    
    // 使用 inline for 遍历
    inline for (tuple, 0..) |value, i| {
        std.debug.print("tuple[{}] = {} (type: {})\n", .{
            i,
            value,
            @TypeOf(value),
        });
    }
}

3.4 元组作为函数参数 #

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

fn printTuple(tuple: anytype) void {
    const fields = @typeInfo(@TypeOf(tuple)).@"struct".fields;
    inline for (fields, 0..) |field, i| {
        std.debug.print("field[{}] = {} (type: {})\n", .{
            i,
            @field(tuple, field.name),
            field.type,
        });
    }
}

pub fn main() void {
    const my_tuple = .{ 42, "Zig", 3.14 };
    printTuple(my_tuple);
}

四、数组与切片转换 #

4.1 数组转切片 #

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

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    
    // 完整切片
    const slice1: []i32 = arr[0..];
    const slice2: []i32 = &arr;
    
    // 部分切片
    const slice3: []i32 = arr[1..4];
    
    std.debug.print("slice1 len: {}\n", .{slice1.len});
    std.debug.print("slice2 len: {}\n", .{slice2.len});
    std.debug.print("slice3 len: {}\n", .{slice3.len});
}

4.2 切片转数组 #

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

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    const slice: []i32 = arr[0..3];
    
    // 切片转数组(需要编译时已知长度)
    const arr2: [3]i32 = slice[0..3].*;
    
    std.debug.print("arr2: {any}\n", .{arr2});
}

五、常用操作 #

5.1 数组连接 #

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

pub fn main() void {
    const arr1 = [_]i32{ 1, 2, 3 };
    const arr2 = [_]i32{ 4, 5, 6 };
    
    // 数组连接
    const combined = arr1 ++ arr2;
    
    std.debug.print("combined: {any}\n", .{combined});
}

5.2 数组重复 #

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

pub fn main() void {
    const arr = [_]i32{ 1, 2 };
    
    // 数组重复
    const repeated = arr ** 3;
    
    std.debug.print("repeated: {any}\n", .{repeated});
}

5.3 搜索元素 #

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

pub fn main() void {
    const arr = [_]i32{ 10, 20, 30, 40, 50 };
    
    // 查找元素
    for (arr, 0..) |value, i| {
        if (value == 30) {
            std.debug.print("Found 30 at index {}\n", .{i});
            break;
        }
    }
    
    // 使用 std.mem.indexOf
    const slice: []const i32 = &arr;
    if (std.mem.indexOfScalar(i32, slice, 30)) |i| {
        std.debug.print("Found 30 at index {}\n", .{i});
    }
}

5.4 排序 #

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

pub fn main() void {
    var arr = [_]i32{ 5, 2, 8, 1, 9, 3 };
    
    // 排序
    std.sort.heap(i32, &arr, {}, std.sort.asc(i32));
    
    std.debug.print("sorted: {any}\n", .{arr});
}

六、总结 #

复合类型要点:

类型 特点
数组 固定长度,编译时确定
切片 动态长度,运行时确定
元组 匿名结构体,可包含不同类型

下一步,让我们学习可选类型!

最后更新:2026-03-27