Skip to content
zig 版本:0.13.0

切片

切片和数组看起来上很像,在实际使用时,你可能会想要使用切片,因为它相对数组来说,要更加灵活!

你可以对数组、切片、数组指针进行切片操作!

接下来我们演示切片的使用方式:

zig
var array = [_]i32{ 1, 2, 3, 4 };

const len: usize = 3;
const slice: []i32 = array[0..len];

for (slice, 0..) |ele, index| {
    print("第{}个元素为:{}\n", .{ index + 1, ele });
}
print("slice 类型为{}\n", .{@TypeOf(slice)});

const slice_2: []i32 = array[0..array.len];
print("slice_2 类型为{}\n", .{@TypeOf(slice_2)});
zig
const print = @import("std").debug.print;

pub fn main() void {
    var array = [_]i32{ 1, 2, 3, 4 };

    const len: usize = 3;
    const slice: []i32 = array[0..len];

    for (slice, 0..) |ele, index| {
        print("第{}个元素为:{}\n", .{ index + 1, ele });
    }
    print("slice 类型为{}\n", .{@TypeOf(slice)});

    const slice_2: []i32 = array[0..array.len];
    print("slice_2 类型为{}\n", .{@TypeOf(slice_2)});
}

打印结果如下:

sh
第1个元素为:1
第2个元素为:2
第3个元素为:3
slice 类型为[]i32
slice_2 类型为[]i32

切片的使用方式就是类似数组,不过[]中的是索引的边界值,遵循“左闭右开”规则。

以上我们对数组取切片,左边界值为0,右边界值为 len 变量。

注意,这里说的是边界值有一个是变量(运行时可知),如果两个边界值均是编译期可知的话,编译器会直接将切片优化为数组指针。

🅿️ 提示

切片的本质:它本质是一个胖指针,包含了一个 指针类型 [*]T 和 长度。

同时,它的指针 slice.ptr 和长度 slice.len 均是可以操作的,但在实践中,请不要操作它们,这容易破坏切片的内部结构(除非你有把握每次都能正确的处理它们)。

切片指针

切片本身除了具有 len 属性外,还具有 ptr 属性,这意味着我们可以通过语法 slice.ptr 来操作切片的指针,它是一个多项指针!

当我们对切片元素取地址(&)时,得到的是单项指针。

同时,切片本身还有边界检查,但是对切片指针做操作则不会有边界检查!

zig
var array = [_]i32{ 1, 2, 3, 4 };

// 边界使用变量,保证切片不会被优化为数组指针
var len: usize = 3;
_ = &len;

var slice = array[0..len];

print("slice 类型为{}\n", .{@TypeOf(slice)});
print("slice.ptr 类型为{}\n", .{@TypeOf(slice.ptr)});
print("slice 的索引 0 取地址,得到指针类型为{}\n", .{@TypeOf(&slice[0])});
zig
const print = @import("std").debug.print;

pub fn main() void {
    var array = [_]i32{ 1, 2, 3, 4 };

    // 边界使用变量,保证切片不会被优化为数组指针
    var len: usize = 3;
    _ = &len;

    var slice = array[0..len];

    print("slice 类型为{}\n", .{@TypeOf(slice)});
    print("slice.ptr 类型为{}\n", .{@TypeOf(slice.ptr)});
    print("slice 的索引 0 取地址,得到指针类型为{}\n", .{@TypeOf(&slice[0])});
}

打印结果如下:

sh
slice 类型为[]i32
slice.ptr 类型为[*]i32
slice 的索引 0 取地址,得到指针类型为*i32

哨兵切片

语法 [:x]T 是一个切片,它具有运行时已知的长度,并且还保证由该长度索引的元素的标记值。该类型不保证在此之前不存在哨兵元素,哨兵终止的切片允许元素访问 len 索引。

哨兵切片也可以使用切片语法 data[start..end :x] 的变体来创建,其中 data 是多项指针、数组或切片,x 是哨兵值。

哨兵切片认定哨兵位置处的元素是哨兵值,如果不是这种情况,则会触发安全保护中的未定义问题。

zig
// 显式声明切片类型
const str_slice: [:0]const u8 = "hello";
print("str_slice类型:{}\n", .{@TypeOf(str_slice)});

var array = [_]u8{ 3, 2, 1, 0, 3, 2, 1, 0 };
const runtime_length: usize = 3;
const slice: [:0]u8 = array[0..runtime_length :0];
print("slice类型:{}\n", .{@TypeOf(slice)});
zig
const print = @import("std").debug.print;

pub fn main() void {
    // 显式声明切片类型
    const str_slice: [:0]const u8 = "hello";
    print("str_slice类型:{}\n", .{@TypeOf(str_slice)});

    var array = [_]u8{ 3, 2, 1, 0, 3, 2, 1, 0 };
    const runtime_length: usize = 3;
    const slice: [:0]u8 = array[0..runtime_length :0];
    print("slice类型:{}\n", .{@TypeOf(slice)});
}

打印结果如下:

sh
str_slice类型:[:0]const u8
slice类型:[:0]u8