文章
Zig中的列表
目录
1. Zig 列表/容器类型概述#
下面从 定义与底层映射、内存布局与所有权、操作与语法 三个维度,逐一介绍 Zig 中常见的五种列表或容器类型:
| 类型 | 简称 |
|---|---|
| 固定长度数组 | [N]T |
| 切片 | []T |
| 动态数组 | std.ArrayList(T) |
| 双端队列 | std.DequeList(T) |
| SIMD 向量 | @Vector(T, N) |
1.1 固定长度数组 [N]T#
- 定义与底层映射
const A = [5]u8; // 在编译期确定长度 5 的无符号字节数组
```
编译后映射为 LLVM <5 x i8>(在结构体或局部变量中则按连续元素展平)。
- 内存布局与所有权
- 存储位置:栈上(或全局/结构体内)
- 所有权:值类型,拷贝即复制所有元素
- 对齐:按元素类型对齐
- 操作与语法
- 索引:
a[2] - 迭代:
for (a) |elem| … - 固定长度,不能增删
- 索引:
1.2 切片 []T#
- 定义与底层映射
var buf: [100]u8 = undefined; const s: []u8 = buf[20..80]; // 指向 buf 的一个子区间 ``` 底层由一个指向元素起始的指针 + 一个长度组成。
- 内存布局与所有权
- 存储位置:指针和长度字段在栈上,数据不归切片所有
- 所有权:借用,不释放底层内存
- 对齐:与底层缓冲区一致
- 操作与语法
- 索引:
s[0]…s[s.len-1] - 迭代:
for (s) |v| … - 不可增删,仅视图
- 索引:
1.3 动态数组 std.ArrayList(T)#
- 定义与底层映射
const std = @import("std"); var list = std.ArrayList(u32).init(std.heap.page_allocator); ``` 底层是一个指针(heap buffer)、当前长度、容量三项。
- 内存布局与所有权
- 存储位置:控制块在栈上,内容在堆上
- 所有权:负责在
deinit()时释放底层堆内存 - 对齐:分配器决定
- 操作与语法
- 尾部追加:
try list.append(x) - 随机访问:
list.items[i] - 弹出末尾:
list.pop() - 可增删,但中间插入/删除复杂度高
- 尾部追加:
1.4 双端队列 std.DequeList(T)#
- 定义与底层映射
const std = @import("std"); var dq = std.DequeList(u8).init(std.heap.page_allocator); ``` 底层通常是分块的环形缓冲区。
- 内存布局与所有权
- 存储位置:控制结构在栈上,块状数据在堆上
- 所有权:
deinit()时释放所有块 - 对齐:分配器决定
- 操作与语法
- 头部/尾部入队:
dq.pushFront(x),dq.pushBack(x) - 头部/尾部出队:
dq.popFront(),dq.popBack() - 随机访问:
dq.items[i] - 适合两端高频增删
- 头部/尾部入队:
1.5 SIMD 向量 @Vector(T, N)#
- 定义与底层映射
const Vec4 = @Vector(f32, 4);
```
编译期映射为 LLVM <4 x float>,在支持的 CPU 上使用 SSE/AVX 等指令。
- 内存布局与所有权
- 存储位置:寄存器或栈
- 所有权:值类型,整体拷贝
- 对齐:与架构寄存器宽度一致
- 操作与语法
- 并行算术:
+ - * / - 重排:
@shuffle(v, mask) - 掩码选取:
@select(mask, a, b) - 归约:手动
v[0] + v[1] + …(或新版Zig@reduce)
- 并行算术:
2. 五种类型对比表#
| 特性 | [N]T | []T | ArrayList(T) | DequeList(T) | @Vector(T,N) |
|---|---|---|---|---|---|
| 长度 | 编译期固定 | 运行时固定 | 运行时可变 | 运行时可变 | 编译期固定 |
| 存储 | 栈/全局 | 指针 + 长度 | 堆(内容)+栈(控件) | 堆(块状数据)+栈(控件) | 寄存器/栈 |
| 所有权 | 值类型 | 借用 | 拥有,需 deinit() | 拥有,需 deinit() | 值类型 |
| 对齐 | 类型对齐 | 与底层一致 | 分配器决定 | 分配器决定 | 寄存器宽度 |
| 增删 | 不支持 | 不支持 | 支持末尾操作 | 支持两端操作 | 不支持 |
| 随机访问 | O(1) | O(1) | O(1) | O(1) | O(1) |
| 迭代开销 | 与标量相同 | 与标量相同 | 略高于切片 | 略高于切片 | 与标量相同 |
| 典型场景 | 编译期常量、协议头 | 视图/函数参数 | 可变集合、尾部追加 | 双端队列操作 | 并行标量运算 |
3. 其他语言中的 SIMD 向量对照#
| 语言/库 | 类型 | 语法示例 |
|---|---|---|
| C / C++ (intrinsics) | __m128 / __m256 | _mm_add_ps(a, b) |
Rust (std::simd) | Simd<f32, 4> | let c = a + b; |
| Swift (Apple SIMD) | SIMD4<Float> | let c = a &+ b |
| .NET (C# System.Numerics) | Vector<float> | var c = a + b; |
| Java Vector API | FloatVector | var c = a.add(b); |
| Julia StaticArrays.jl | SVector{4, Float32} | c = a + b |
| GLSL / HLSL (Shaders) | vec4 / float4 | vec4 c = a + b; |
| Python (NumPy ndarray) | numpy.ndarray | c = a + b (广播) |
现代语言都在尝试“一套代码映射最优指令集”,以提升可移植性与性能。#
4. 场景选型建议#
- 批量并行标量运算:
@Vector(T, N) - 编译期、定长数据:
[N]T - 借用/视图已有缓冲:
[]T - 动态可变集合(尾部增删多):
std.ArrayList(T) - 双端高效增删:
std.DequeList(T)通过以上分层介绍与对比,你即可根据数据特性、内存语义、性能需求等维度,灵活选用最合适的 Zig 容器与向量类型。