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 的结构体和枚举。