Skip to content

Zig 数组与切片

数组和切片是 Zig 中处理序列数据的核心数据结构。本章将详细介绍它们的使用方法和最佳实践。

数组基础

数组声明和初始化

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

pub fn main() void {
    // 显式指定大小的数组
    const explicit_array: [5]i32 = [5]i32{ 1, 2, 3, 4, 5 };
    
    // 推断大小的数组
    const inferred_array = [_]i32{ 10, 20, 30, 40, 50 };
    
    // 重复初始化
    const repeated_array = [_]i32{42} ** 8;
    
    // 部分初始化(其余为零值)
    const partial_array = [10]i32{ 1, 2, 3 };
    
    std.debug.print("显式数组: ");
    for (explicit_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    std.debug.print("推断数组: ");
    for (inferred_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    std.debug.print("重复数组: ");
    for (repeated_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    std.debug.print("部分初始化: ");
    for (partial_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
}

数组属性和操作

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

pub fn main() void {
    var numbers = [_]i32{ 1, 2, 3, 4, 5 };
    
    // 数组长度
    std.debug.print("数组长度: {}\n", .{numbers.len});
    
    // 数组元素访问
    std.debug.print("第一个元素: {}\n", .{numbers[0]});
    std.debug.print("最后一个元素: {}\n", .{numbers[numbers.len - 1]});
    
    // 修改数组元素
    numbers[2] = 100;
    std.debug.print("修改后的数组: ");
    for (numbers) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    // 数组比较
    const array1 = [_]i32{ 1, 2, 3 };
    const array2 = [_]i32{ 1, 2, 3 };
    const array3 = [_]i32{ 1, 2, 4 };
    
    std.debug.print("array1 == array2: {}\n", .{std.mem.eql(i32, &array1, &array2)});
    std.debug.print("array1 == array3: {}\n", .{std.mem.eql(i32, &array1, &array3)});
}

多维数组

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

pub fn main() void {
    // 二维数组
    const matrix = [3][3]i32{
        [3]i32{ 1, 2, 3 },
        [3]i32{ 4, 5, 6 },
        [3]i32{ 7, 8, 9 },
    };
    
    std.debug.print("3x3 矩阵:\n");
    for (matrix, 0..) |row, i| {
        std.debug.print("行 {}: ", .{i});
        for (row) |item| {
            std.debug.print("{:2} ", .{item});
        }
        std.debug.print("\n");
    }
    
    // 三维数组
    const cube = [2][2][2]i32{
        [2][2]i32{
            [2]i32{ 1, 2 },
            [2]i32{ 3, 4 },
        },
        [2][2]i32{
            [2]i32{ 5, 6 },
            [2]i32{ 7, 8 },
        },
    };
    
    std.debug.print("\n2x2x2 立方体:\n");
    for (cube, 0..) |layer, i| {
        std.debug.print("层 {}:\n", .{i});
        for (layer, 0..) |row, j| {
            std.debug.print("  行 {}: ", .{j});
            for (row) |item| {
                std.debug.print("{} ", .{item});
            }
            std.debug.print("\n");
        }
    }
}

切片基础

切片创建和使用

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

pub fn main() void {
    var array = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
    // 创建切片
    const full_slice: []i32 = array[0..];           // 完整切片
    const partial_slice: []i32 = array[2..7];       // 部分切片
    const end_slice: []i32 = array[5..];            // 从索引5到结尾
    const start_slice: []i32 = array[0..5];         // 从开始到索引4
    
    std.debug.print("原数组: ");
    for (array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    std.debug.print("完整切片: ");
    for (full_slice) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    std.debug.print("部分切片 [2..7]: ");
    for (partial_slice) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    std.debug.print("结尾切片 [5..]: ");
    for (end_slice) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    std.debug.print("开始切片 [0..5]: ");
    for (start_slice) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
}

切片属性和操作

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

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
    var slice: []u8 = data[2..8];
    
    // 切片属性
    std.debug.print("切片长度: {}\n", .{slice.len});
    std.debug.print("切片指针: {*}\n", .{slice.ptr});
    
    // 切片内容
    std.debug.print("切片内容: ");
    for (slice) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    // 修改切片(会影响原数组)
    slice[0] = 99;
    slice[slice.len - 1] = 88;
    
    std.debug.print("修改切片后的原数组: ");
    for (data) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    // 子切片
    const sub_slice = slice[1..4];
    std.debug.print("子切片: ");
    for (sub_slice) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
}

字符串作为切片

字符串切片操作

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

pub fn main() void {
    const text = "Hello, Zig Programming!";
    
    // 字符串是 []const u8 类型的切片
    std.debug.print("完整字符串: {s}\n", .{text});
    std.debug.print("字符串长度: {}\n", .{text.len});
    
    // 字符串切片
    const hello = text[0..5];
    const zig = text[7..10];
    const programming = text[11..22];
    
    std.debug.print("Hello: {s}\n", .{hello});
    std.debug.print("Zig: {s}\n", .{zig});
    std.debug.print("Programming: {s}\n", .{programming});
    
    // 字符访问
    std.debug.print("第一个字符: {c} (ASCII: {})\n", .{ text[0], text[0] });
    std.debug.print("最后一个字符: {c} (ASCII: {})\n", .{ text[text.len - 1], text[text.len - 1] });
    
    // 遍历字符
    std.debug.print("逐字符输出: ");
    for (text) |char| {
        std.debug.print("{c}", .{char});
    }
    std.debug.print("\n");
}

字符串处理函数

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

// 查找子字符串
fn findSubstring(haystack: []const u8, needle: []const u8) ?usize {
    if (needle.len > haystack.len) return null;
    
    for (haystack, 0..) |_, i| {
        if (i + needle.len > haystack.len) break;
        if (std.mem.eql(u8, haystack[i..i + needle.len], needle)) {
            return i;
        }
    }
    return null;
}

// 计算字符出现次数
fn countChar(text: []const u8, char: u8) usize {
    var count: usize = 0;
    for (text) |c| {
        if (c == char) count += 1;
    }
    return count;
}

// 反转字符串
fn reverseString(allocator: std.mem.Allocator, text: []const u8) ![]u8 {
    var reversed = try allocator.alloc(u8, text.len);
    for (text, 0..) |char, i| {
        reversed[text.len - 1 - i] = char;
    }
    return reversed;
}

pub fn main() !void {
    const text = "Hello, Zig! Zig is great!";
    
    // 查找子字符串
    if (findSubstring(text, "Zig")) |index| {
        std.debug.print("找到 'Zig' 在位置: {}\n", .{index});
    }
    
    // 计算字符出现次数
    const count = countChar(text, 'g');
    std.debug.print("字符 'g' 出现次数: {}\n", .{count});
    
    // 反转字符串
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    const reversed = try reverseString(allocator, "Hello");
    defer allocator.free(reversed);
    
    std.debug.print("'Hello' 反转后: {s}\n", .{reversed});
}

数组和切片的内存布局

内存连续性

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

pub fn main() void {
    var array = [_]i32{ 1, 2, 3, 4, 5 };
    
    std.debug.print("数组元素地址:\n");
    for (array, 0..) |*item, i| {
        std.debug.print("array[{}] = {} (地址: {*})\n", .{ i, item.*, item });
    }
    
    // 验证内存连续性
    const ptr0 = @intFromPtr(&array[0]);
    const ptr1 = @intFromPtr(&array[1]);
    const element_size = @sizeOf(i32);
    
    std.debug.print("\n内存连续性验证:\n");
    std.debug.print("元素大小: {} 字节\n", .{element_size});
    std.debug.print("地址差: {} 字节\n", .{ptr1 - ptr0});
    std.debug.print("内存连续: {}\n", .{ptr1 - ptr0 == element_size});
    
    // 切片的内存布局
    const slice: []i32 = array[1..4];
    std.debug.print("\n切片信息:\n");
    std.debug.print("切片指针: {*}\n", .{slice.ptr});
    std.debug.print("切片长度: {}\n", .{slice.len});
    std.debug.print("切片大小: {} 字节\n", .{@sizeOf(@TypeOf(slice))});
}

动态数组 (ArrayList)

ArrayList 基本使用

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    // 创建动态数组
    var list = std.ArrayList(i32).init(allocator);
    defer list.deinit();
    
    // 添加元素
    try list.append(10);
    try list.append(20);
    try list.append(30);
    
    std.debug.print("初始列表: ");
    for (list.items) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    // 插入元素
    try list.insert(1, 15);
    std.debug.print("插入15后: ");
    for (list.items) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    // 删除元素
    _ = list.orderedRemove(2);
    std.debug.print("删除索引2后: ");
    for (list.items) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    // 批量添加
    try list.appendSlice(&[_]i32{ 40, 50, 60 });
    std.debug.print("批量添加后: ");
    for (list.items) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    std.debug.print("列表长度: {}\n", .{list.items.len});
    std.debug.print("列表容量: {}\n", .{list.capacity});
}

ArrayList 高级操作

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    var numbers = std.ArrayList(i32).init(allocator);
    defer numbers.deinit();
    
    // 预分配容量
    try numbers.ensureTotalCapacity(100);
    std.debug.print("预分配容量: {}\n", .{numbers.capacity});
    
    // 批量初始化
    for (0..10) |i| {
        try numbers.append(@intCast(i * 10));
    }
    
    // 查找元素
    const target = 50;
    var found_index: ?usize = null;
    for (numbers.items, 0..) |item, i| {
        if (item == target) {
            found_index = i;
            break;
        }
    }
    
    if (found_index) |index| {
        std.debug.print("找到 {} 在索引 {}\n", .{ target, index });
    }
    
    // 排序
    std.mem.sort(i32, numbers.items, {}, comptime std.sort.asc(i32));
    std.debug.print("排序后: ");
    for (numbers.items) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    // 反转
    std.mem.reverse(i32, numbers.items);
    std.debug.print("反转后: ");
    for (numbers.items) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    // 清空但保留容量
    numbers.clearRetainingCapacity();
    std.debug.print("清空后 - 长度: {}, 容量: {}\n", .{ numbers.items.len, numbers.capacity });
}

数组和切片的算法

搜索算法

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

// 线性搜索
fn linearSearch(comptime T: type, array: []const T, target: T) ?usize {
    for (array, 0..) |item, i| {
        if (item == target) return i;
    }
    return null;
}

// 二分搜索(要求数组已排序)
fn binarySearch(comptime T: type, array: []const T, target: T) ?usize {
    var left: usize = 0;
    var right: usize = array.len;
    
    while (left < right) {
        const mid = left + (right - left) / 2;
        if (array[mid] == target) {
            return mid;
        } else if (array[mid] < target) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return null;
}

pub fn main() void {
    const unsorted_array = [_]i32{ 64, 34, 25, 12, 22, 11, 90, 88, 76, 50, 42 };
    const sorted_array = [_]i32{ 11, 12, 22, 25, 34, 42, 50, 64, 76, 88, 90 };
    
    // 线性搜索
    const target1 = 22;
    if (linearSearch(i32, &unsorted_array, target1)) |index| {
        std.debug.print("线性搜索: 找到 {} 在位置 {}\n", .{ target1, index });
    }
    
    // 二分搜索
    const target2 = 42;
    if (binarySearch(i32, &sorted_array, target2)) |index| {
        std.debug.print("二分搜索: 找到 {} 在位置 {}\n", .{ target2, index });
    }
    
    // 搜索不存在的元素
    const target3 = 99;
    if (linearSearch(i32, &unsorted_array, target3)) |index| {
        std.debug.print("找到 {} 在位置 {}\n", .{ target3, index });
    } else {
        std.debug.print("未找到 {}\n", .{target3});
    }
}

排序算法

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

// 冒泡排序
fn bubbleSort(comptime T: type, array: []T) void {
    const n = array.len;
    for (0..n) |i| {
        for (0..n - i - 1) |j| {
            if (array[j] > array[j + 1]) {
                const temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}

// 选择排序
fn selectionSort(comptime T: type, array: []T) void {
    const n = array.len;
    for (0..n) |i| {
        var min_idx = i;
        for (i + 1..n) |j| {
            if (array[j] < array[min_idx]) {
                min_idx = j;
            }
        }
        if (min_idx != i) {
            const temp = array[i];
            array[i] = array[min_idx];
            array[min_idx] = temp;
        }
    }
}

// 插入排序
fn insertionSort(comptime T: type, array: []T) void {
    for (1..array.len) |i| {
        const key = array[i];
        var j: isize = @intCast(i - 1);
        
        while (j >= 0 and array[@intCast(j)] > key) {
            array[@intCast(j + 1)] = array[@intCast(j)];
            j -= 1;
        }
        array[@intCast(j + 1)] = key;
    }
}

pub fn main() void {
    // 测试冒泡排序
    var bubble_array = [_]i32{ 64, 34, 25, 12, 22, 11, 90 };
    std.debug.print("冒泡排序前: ");
    for (bubble_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    bubbleSort(i32, &bubble_array);
    std.debug.print("冒泡排序后: ");
    for (bubble_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n\n");
    
    // 测试选择排序
    var selection_array = [_]i32{ 64, 25, 12, 22, 11 };
    std.debug.print("选择排序前: ");
    for (selection_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    selectionSort(i32, &selection_array);
    std.debug.print("选择排序后: ");
    for (selection_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n\n");
    
    // 测试插入排序
    var insertion_array = [_]i32{ 12, 11, 13, 5, 6 };
    std.debug.print("插入排序前: ");
    for (insertion_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
    
    insertionSort(i32, &insertion_array);
    std.debug.print("插入排序后: ");
    for (insertion_array) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n");
}

数组和切片的最佳实践

1. 选择合适的数据结构

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    // ✅ 固定大小,已知数据 -> 使用数组
    const fixed_data = [_]i32{ 1, 2, 3, 4, 5 };
    std.debug.print("固定数组: {} 个元素\n", .{fixed_data.len});
    
    // ✅ 动态大小,频繁修改 -> 使用 ArrayList
    var dynamic_data = std.ArrayList(i32).init(allocator);
    defer dynamic_data.deinit();
    
    for (0..10) |i| {
        try dynamic_data.append(@intCast(i));
    }
    std.debug.print("动态数组: {} 个元素\n", .{dynamic_data.items.len});
    
    // ✅ 只读访问,部分数据 -> 使用切片
    const slice_data: []const i32 = dynamic_data.items[2..7];
    std.debug.print("切片: {} 个元素\n", .{slice_data.len});
}

2. 边界检查和安全访问

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

fn safeGet(comptime T: type, array: []const T, index: usize) ?T {
    if (index >= array.len) return null;
    return array[index];
}

fn safeSet(comptime T: type, array: []T, index: usize, value: T) bool {
    if (index >= array.len) return false;
    array[index] = value;
    return true;
}

pub fn main() void {
    var data = [_]i32{ 10, 20, 30, 40, 50 };
    
    // ✅ 安全访问
    if (safeGet(i32, &data, 2)) |value| {
        std.debug.print("安全获取 data[2] = {}\n", .{value});
    }
    
    if (safeGet(i32, &data, 10)) |value| {
        std.debug.print("不会执行: {}\n", .{value});
    } else {
        std.debug.print("索引 10 超出范围\n", .{});
    }
    
    // ✅ 安全设置
    if (safeSet(i32, &data, 1, 99)) {
        std.debug.print("成功设置 data[1] = 99\n", .{});
    }
    
    if (!safeSet(i32, &data, 10, 99)) {
        std.debug.print("设置 data[10] 失败:索引超出范围\n", .{});
    }
}

3. 内存效率

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    // ✅ 预分配容量避免频繁重新分配
    var efficient_list = std.ArrayList(i32).init(allocator);
    defer efficient_list.deinit();
    
    try efficient_list.ensureTotalCapacity(1000); // 预分配
    
    for (0..1000) |i| {
        efficient_list.appendAssumeCapacity(@intCast(i)); // 无需检查容量
    }
    
    std.debug.print("高效列表: {} 个元素,容量: {}\n", 
                    .{ efficient_list.items.len, efficient_list.capacity });
    
    // ✅ 使用切片避免不必要的复制
    const slice = efficient_list.items[100..200];
    std.debug.print("切片处理: {} 个元素\n", .{slice.len});
}

实际应用示例

矩阵运算

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

const Matrix = struct {
    data: []f64,
    rows: usize,
    cols: usize,
    allocator: std.mem.Allocator,
    
    const Self = @This();
    
    pub fn init(allocator: std.mem.Allocator, rows: usize, cols: usize) !Self {
        const data = try allocator.alloc(f64, rows * cols);
        @memset(data, 0.0);
        
        return Self{
            .data = data,
            .rows = rows,
            .cols = cols,
            .allocator = allocator,
        };
    }
    
    pub fn deinit(self: *Self) void {
        self.allocator.free(self.data);
    }
    
    pub fn get(self: *const Self, row: usize, col: usize) f64 {
        return self.data[row * self.cols + col];
    }
    
    pub fn set(self: *Self, row: usize, col: usize, value: f64) void {
        self.data[row * self.cols + col] = value;
    }
    
    pub fn print(self: *const Self) void {
        for (0..self.rows) |i| {
            for (0..self.cols) |j| {
                std.debug.print("{d:6.2} ", .{self.get(i, j)});
            }
            std.debug.print("\n");
        }
    }
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    var matrix = try Matrix.init(allocator, 3, 3);
    defer matrix.deinit();
    
    // 初始化矩阵
    for (0..3) |i| {
        for (0..3) |j| {
            matrix.set(i, j, @floatFromInt(i * 3 + j + 1));
        }
    }
    
    std.debug.print("3x3 矩阵:\n");
    matrix.print();
}

总结

本章详细介绍了 Zig 的数组与切片:

  • ✅ 数组的声明、初始化和操作
  • ✅ 多维数组的使用
  • ✅ 切片的创建和属性
  • ✅ 字符串作为切片的处理
  • ✅ 内存布局和连续性
  • ✅ 动态数组 ArrayList 的使用
  • ✅ 搜索和排序算法
  • ✅ 最佳实践和实际应用

数组和切片是 Zig 中最基础也是最重要的数据结构,掌握它们的使用对于编写高效的 Zig 程序至关重要。在下一章中,我们将学习 Zig 的结构体和枚举。

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