Zig从XX到放弃(3)数组与指针

数组

数组是一组固定大小的元素,数组的大小必须是常量表达式。数组的大小是usize类型,用数组的len获得。

为了创建一个数组,可以使用常量表达式的数组字面量,或者使用std.mem.zeroesstd.mem.alloc

const a = [3]i32{1,2,3};
const b = [_]i32{4, 5, 6};
const c: [3]i32 = .{7, 8, 9};

看看上面的三个表达式,其含义和目的都是一样的。第一个表达式省略了变量的类型,编译器会根据数组字面量的内容推断出类型。第二个表达式使用了_作为数组的大小,这个_会被替换成数组字面量的长度。第三个表达式使用了类型注解,这个类型注解必须和数组字面量的内容匹配,后面的.{}是一个结构体字面量,这个结构体字面量的类型是[3]i32

此外还能创建一个内容为空的数组,这个数组的大小必须是常量表达式。

var d: [3]i32 = undefined;
d = .{ 10, 11, 12 };

数组常量有两个运算符很常用,**++**是数组的拷贝,++是数组的连接。

const e = .{1} ** 3;
const f = .{1} ** 3 ++ .{2} ** 2 ++ .{3};

**++优先级高,所以a ** b ++ c会被解析成(a ** b) ++ c

输出上面的数组,值得注意的是,这段代码里演示了多维数组,数组的里面是[]const i32类型,外面是一个长度为6的数组。里面的这个而类型其实是一个叫slice的东西,这个东西本质上是一个结构体,里面有一个指针和一个长度,这个指针[*]T指向了一个数组的一部分(这里就是指向第一个元素&a),这个长度就是这个slice的长度。

这个东西一开始不好理解,后面用着用着就习惯。下面一节也做一点小小的探索。

for ([_][]const i32{ &a, &b, &c, &d, &e, &f }) |arr| {
    print("{d:<4}{any}", .{ arr.len, arr });
    print("\n", .{});
}

得到数组的大小和内容。

3   { 1, 2, 3 }
3   { 4, 5, 6 }
3   { 7, 8, 9 }
3   { 10, 11, 12 }
3   { 1, 1, 1 }
6   { 1, 1, 1, 2, 2, 3 }

指针与slice

指针在Zig中有两类:

  • *T:指向T类型的指针
  • [*]T:指向T类型的数组的指针

前者的访问方式是ptr.*,后者的访问方式有几种:

  • ptr[i]
  • ptr[start_index..end_index]
  • ptr+x,ptr-x

这里唯一需要注意的是,T的尺寸必须在编译时已知。

与指针不同,[]T是一个slice,上面说过,slice是一个结构体,里面有一个指针和一个长度,这个指针指向了一个数组的一部分,这个长度就是这个slice的长度。slice的访问方式有几种:

  • slice[i]
  • slice[start_index..end_index]
  • slice.len

还有一个概念就是*[N]T,这个是一个指向T类型的数组的指针,这个数组的大小是也是已知的。比如前面for循环里构造的那个[_][]const i32,它的每个元素就是一个*[N]T类型的指针。所以需要用&取地址。这个指针由于尺寸已知,也能用下面的集中访问方式:

  • ptr[i]
  • ptr[start_index..end_index]
  • ptr.len

下面用一段代码来探索一下这几个概念。定义函数,输出类型的大小和位数。

fn show_ptr_arr_size(comptime T: type) void {
    const array_typs = [_]type{ T, []T, [][]T, [][][]T, *T, *[]T, *[][]T, *[][][]T, [10]T, *[10]T, usize };

    print("|{s:>12}|{s:>10}|{s:>10}|\n", .{ "type", "size", "bitSize" });
    print("|{s:>12}|{s:>10}|{s:>10}|\n", .{ "-" ** 8, "-" ** 10, "-" ** 10 });
    inline for (array_typs) |typ| {
        print("|{s:>12}|{d:>10}|{d:>10}|\n", .{ @typeName(typ), @sizeOf(typ), @bitSizeOf(typ) });
    }
}

```zig

在主程序里面调用这个函数。

```zig
pub fn main() void {
    show_ptr_arr_size(u8);
    show_ptr_arr_size(u16);
}

得到下面的结果,与前面的说法时相符的。数组和slice包含了一个指针,一个usize,所以它的大小是16字节,位数是128位。而指针则不包含长度,所以它的大小是8字节,位数是64位。

typesizebitSize
u818
[]u816128
[][]u816128
[][][]u816128
*u8864
*[]u8864
*[][]u8864
*[][][]u8864
[10]u81080
*[10]u8864
usize864
typesizebitSize
u16216
[]u1616128
[][]u1616128
[][][]u1616128
*u16864
*[]u16864
*[][]u16864
*[][][]u16864
[10]u1620160
*[10]u16864
usize864

这两个表中唯一不理解的是,*[10]T的大小为什么是8字节,而不是16字节。按照文档的描述,这个指针可以操作array_ptr.len,那么这个长度记录在哪里呢?

结论

  • 数组在Zig中是一个结构体,它包含了一个指针和一个长度。
  • 数组和指针十分相关,但是访问方式有一点点不同。
  • *[N]T到底怎么回事?
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大福是小强

除非你钱多烧得慌……

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值