Zig的数组是编译期确定长度的,长度是类型的一部分:[5]u8和[10]u8是不同类型。
const std = @import("std");
pub fn main() !void {
// 声明数组
const arr1 = [5]i32{ 1, 2, 3, 4, 5 };
const arr2 = [_]i32{ 10, 20, 30 }; // 自动推断长度
// 零初始化
var buf: [1024]u8 = 0; // 全部填0
var buf2: [64]u8 = undefined;
// 重复初始化
const ones = [10]u8{0xFF} ** 3; // [0xFF] 重复3次
// 访问和修改
var nums = [_]i32{ 1, 2, 3 };
nums[0] = 42; // 修改
const first = nums[0]; // 读取
// 编译期已知边界的安全检查
// nums[3] = 0; // 编译错误!越界
// 获取数组长度
const len = nums.len; // 3 (comptime已知)
// 遍历
for (arr1, 0..) |val, i| {
std.debug.print("[{}] = {}\n", .{ i, val });
}
// 数组拼接(编译期)
const combined = arr1 ++ arr2;
// 数组指针解引用
const ptr: *const [5]i32 = &arr1;
std.debug.print("ptr[0] = {}\n", .{ptr[0]});
}
切片是运行时确定长度的视图,包含指针和长度。相当于胖指针。
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// 从数组创建切片
var arr = [10]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
const full: []i32 = &arr; // 全数组切片
const sub: []i32 = arr[2..7]; // [3,4,5,6,7]
const head: []i32 = arr[0..3]; // [1,2,3]
const tail: []i32 = arr[7..]; // [8,9,10]
// 堆分配切片
const heap_slice = try allocator.alloc(i32, 100);
defer allocator.free(heap_slice);
// 初始化
@memset(heap_slice, 0);
// 切片操作
std.debug.print("len={}, ptr={*}\n", .{ sub.len, sub.ptr });
// 切片重新切分
const inner = sub[1..3]; // [4,5]
// 切片作为函数参数(不像数组那样拷贝)
printSlice(full);
}
fn printSlice(items: []const i32) void {
for (items) |item| {
std.debug.print("{} ", .{item});
}
std.debug.print("\n", .{});
}
Zig有多种指针类型,每种都有明确语义:
const std = @import("std");
pub fn main() !void {
var x: i32 = 42;
// 单项指针
const ptr1: *i32 = &x; // 可变指针
const ptr2: *const i32 = &x; // 只读指针
// 解引用
ptr1.* = 100; // 通过指针修改
std.debug.print("x = {}\n", .{ptr2.*}); // 100
// 切片指针(最常用)
var arr = [5]i32{ 1, 2, 3, 4, 5 };
const slice_ptr: []i32 = &arr;
// 指针算术(通过slice,不直接对指针做)
const offset = slice_ptr[2..]; // 从第3个开始
// 可选指针(可为null)
var maybe_ptr: ?*i32 = &x;
maybe_ptr = null;
// C指针(与C互操作时使用)
// var c_ptr: [*c]i32 = &x;
_ = offset;
}
*align(1)显式声明Zig支持sentinel-terminated数组和切片,最常见的是C字符串(以0结尾):
const std = @import("std");
pub fn main() !void {
// Sentinel-terminated数组
const c_str: [5:0]u8 = .{ 'h', 'e', 'l', 'l', 'o' };
// 内存: [h, e, l, l, o, \0] -- 6字节!
// Sentinel-terminated切片
const msg: [:0]const u8 = "hello"; // 字面量就是[:0]const u8
// 与C互操作时传递
const c_ptr: [*:0]const u8 = c_str;
// 互转
const regular_slice: []const u8 = c_str[0..5]; // 去掉sentinel
const sentinel_slice: [:0]const u8 = std.mem.sliceTo(c_str, 0);
// 自定义sentinel值
const paths: [:'\n']const u8 = "/usr/bin";
_ = regular_slice;
_ = sentinel_slice;
_ = paths;
}
// 二维数组(行优先)
const matrix = [3][3]f32{
.{ 1.0, 0.0, 0.0 },
.{ 0.0, 1.0, 0.0 },
.{ 0.0, 0.0, 1.0 },
};
// 矩阵乘法
fn matMul(comptime N: usize, a: [N][N]f32, b: [N][N]f32) [N][N]f32 {
var result: [N][N]f32 = 0;
for (0..N) |i| {
for (0..N) |j| {
for (0..N) |k| {
result[i][j] += a[i][k] * b[k][j];
}
}
}
return result;
}
| 方式 | Debug | ReleaseFast |
|---|---|---|
| Zig for循环 | 2.8 | 0.4 |
| Zig while + 索引 | 2.8 | 0.4 |
| C for循环 | - | 0.4 |
| Rust iter() | - | 0.5 |
reverse(arr: []i32)原地反转数组std.mem.len,手写函数计算[:0]const u8的长度0xFF为sentinel的u8数组,编写遍历函数