
Zig 学习笔记(v0.16 入门篇)—— 基本类型
适用环境:Windows 11 + Zig 0.16
笔记风格:零基础友好,通俗解释 + 精确概念 + 避坑指南
一、概述
在 Zig 中,一切数据都有明确的类型。Zig 是一门静态类型语言——变量的类型在编译时就已确定,不会在运行时改变。
Zig 类型系统的特点:
| 特点 | 说明 |
|---|---|
| 静态类型 | 所有变量的类型在编译期已知 |
| 强类型 | 不同类型之间不会自动转换,必须显式转换 |
| 类型推断 | 编译器可根据初始值自动推断类型 |
| 编译期反射 | 可在编译时检查和操作类型信息 |
基本类型分类:
Zig 基本类型
├── 标量类型(Scalar)—— 单一值
│ ├── 整数(Integer):i8, u8, i32, u32, ...
│ ├── 浮点数(Float):f16, f32, f64, f128
│ ├── 布尔值(Boolean):bool
│ └── 字符(Character):Unicode 码位字面量
├── 复合类型(Compound)
│ ├── 数组(Array):固定长度
│ ├── 切片(Slice):动态长度视图
│ └── 向量(Vector):SIMD 并行计算
├── 特殊类型
│ ├── 可选类型(Optional):?T = null 或 T
│ ├── 错误联合(Error Union):!T = error 或 T
│ ├── void:无值
│ └── 零大小类型(Zero-Sized Types)
└── 类型间的类型
├── comptime_int / comptime_float
├── type(类型本身也是值)
└── anytype(编译期泛型参数)
二、整数类型(Integer)
2.1 固定宽度整数
Zig 的整数类型划分非常详细,位宽明确,没有模糊不清的 int 或 long。
| 类型 | 对应 C 类型 | 描述 |
|---|---|---|
i8 | int8_t | 有符号 8 位整数(-128 ~ 127) |
u8 | uint8_t | 无符号 8 位整数(0 ~ 255) |
i16 | int16_t | 有符号 16 位整数 |
u16 | uint16_t | 无符号 16 位整数 |
i32 | int32_t | 有符号 32 位整数 |
u32 | uint32_t | 无符号 32 位整数 |
i64 | int64_t | 有符号 64 位整数 |
u64 | uint64_t | 无符号 64 位整数 |
i128 | __int128 | 有符号 128 位整数 |
u128 | unsigned __int128 | 无符号 128 位整数 |
| |
2.2 指针大小整数
isize 和 usize 的大小取决于目标 CPU 架构:32 位系统上是 32 位,64 位系统上是 64 位。
| 类型 | 描述 |
|---|---|
isize | 有符号指针大小的整数 |
usize | 无符号指针大小的整数,常用于数组索引和长度 |
| |
2.3 任意位宽整数
Zig 支持任意位宽的整数,在 u(无符号)或 i(有符号)后直接加数字即可:
| |
⚠️ 整数类型允许的最大位宽为 65535。
2.4 整数字面量(Literals)
整数字面量默认类型为 comptime_int,可以使用多种进制书写:
| 进制 | 示例 | 说明 |
|---|---|---|
| 十进制 | 98222 | 默认 |
| 十六进制 | 0xff | 以 0x 开头 |
| 八进制 | 0o755 | 以 0o 开头 |
| 二进制 | 0b11110000 | 以 0b 开头 |
数字中的下划线可作为视觉分隔符,不影响数值:
| |
2.5 ⚠️ 整数溢出与除零:构建模式决定行为
除零检测
| |
整数溢出行为 vs 构建模式
| 构建模式 | 整数溢出 / 除零 | 说明 |
|---|---|---|
Debug | Panic(程序崩溃并报错) | 开发时保护你,帮你及早发现问题 |
ReleaseSafe | Panic(程序崩溃并报错) | 保持安全检查,适合生产环境 |
ReleaseFast | 未定义行为(UB) | 追求极致性能,不再检查 |
ReleaseSmall | 未定义行为(UB) | 追求最小体积,不再检查 |
💀 铁律:永远不要依赖溢出或除法行为来设计程序逻辑。如果需要“回绕”效果,请使用
@addWithOverflow等显式操作。
| |
三、浮点数类型(Float)
Zig 支持 IEEE 754 标准的浮点数:
| 类型 | 精度 | 描述 |
|---|---|---|
f16 | 半精度 | 16 位浮点数 |
f32 | 单精度 | 32 位浮点数 |
f64 | 双精度 | 64 位浮点数 |
f128 | 四倍精度 | 128 位浮点数 |
| |
浮点数字面量的默认类型是 comptime_float,内部是 f128。
⚠️ 注意:
comptime_float不能隐式转换为整数,即使它保存的是整数值(如3.0)。
| |
四、布尔类型(Boolean)
一句话:
bool类型只有两个值:true和false。
| |
布尔运算:
| |
⚠️ 特别警示:
bool类型在内存中必须是 0 或 1。如果通过undefined或不当的指针操作让bool变成其他值,读取时会触发 CPU 异常(SigTrap)!参见上一章笔记第 4.4 节。
五、字符与字符串
5.1 Unicode 码位字面量
Zig 没有专门的 char 类型。最接近的是 Unicode 码位字面量——用单引号包围一个 Unicode 字符,得到的是一个 comptime_int 类型的值,表示该字符的 Unicode 码位:
| |
⚠️ 注意:某些看似“单个”的字符(如 emoji 🇨🇳、👨👩👦)实际上由多个码位组合而成,不能写成单个码位字面量。
5.2 字符串字面量(Sentinel 终结符)
字符串字面量用双引号包围,存储的是 UTF-8 编码的字节序列,以 NUL(\0) 结尾:
| |
💡
*const [N:0]u8中:0称为 Sentinel(哨兵),代表末尾有一个值为 0 的字节。这个哨兵让字符串可以直接传给 C 函数而不需要额外复制。
隐式转换规则:
| |
一句话记忆:字符串字面量是“自带 NUL 终结符的只读数组指针”,而
[]const u8是“单纯的字节视图”。
5.3 C 语言交互:c_char
Zig 提供了 c_char 类型,对应 C 语言的 char 类型,仅在需要与 C 交互时使用。
⚠️ 跨平台注意:
c_char的符号性(是有符号还是无符号)取决于目标平台(ABI)。在 Windows 上通常是u8,在 Linux/macOS 上通常是i8。编写跨平台 C 库绑定时,请始终使用c_char而不是u8或i8。
六、可选类型(Optional)
一句话:
?T表示“这个值要么是T类型,要么是null”。
6.1 为什么需要可选类型?
null(空引用)是许多运行时异常的根源。Zig 通过可选类型,在类型系统中区分“可能有值”和“一定有值”,让编译器帮你检查。
6.2 基本用法
| |
⚠️ Zig 保证
null不会被赋值给非可选类型的变量。
6.3 解构可选类型
方式一:orelse —— 提供默认值(最推荐)
| |
✅ 推荐理由:
orelse是专门为“有值取值,无值取默认”这个场景设计的,代码最简短、意图最清晰。
方式二:if 语句解构 —— 只在有值时执行
| |
方式三:if 语句 + else 的变体(不推荐用于简单默认值)
| |
七、错误联合类型(Error Union)
一句话:
!T表示“这个操作要么成功返回T类型的值,要么返回一个错误”。
7.1 定义错误
使用 error 关键字定义错误集合:
| |
7.2 返回错误联合
| |
7.3 处理错误联合
方式一:try —— 出错时向上层抛出
| |
方式二:catch —— 出错时提供替代值
| |
方式三:if 语句解构
| |
八、数组(Array)
一句话:数组是固定长度的、同类型元素的集合。
8.1 定义数组
| |
8.2 访问数组元素
| |
8.3 数组是值类型
数组是值类型——赋值或传参时会复制整个数组:
| |
九、切片(Slice)
一句话:切片是数组的动态视图——它不拥有数据,只是“指向一段连续数据的指针 + 长度”。
9.1 为什么用切片?
数组长度在编译期固定,不够灵活。切片让你可以在运行时决定要操作的数据范围。
9.2 创建切片
从数组创建切片,使用 [start..end] 语法,遵循 “左闭右开” 原则:
| |
9.3 切片 vs 数组指针
| 情况 | 结果类型 | 说明 |
|---|---|---|
| 边界值都是编译期常量 | *[N]T(数组指针) | 长度在编译期已知 |
| 至少一个边界值是运行时变量 | []T(切片) | 长度在运行时确定 |
| |
9.4 切片的内部结构
切片是一个 “胖指针” ,包含:
ptr:指向数据的多项指针(many-item pointer)len:数据长度
| |
💡 理解
[*]i32vs*i32:
[*]i32(many-item pointer):指向多个连续的i32,可以执行指针算术(如ptr + 1),但不携带长度信息——这就是 C 语言里的int*。*i32(single-item pointer):指向单个i32,不能执行指针算术。slice.ptr暴露的是底层的多项指针,而&slice[0]取的是第一个元素的地址(单项指针)。虽然它们的值(内存地址)相同,但类型语义不同。
⚠️ 警告:虽然
slice.ptr和slice.len可以访问和修改,但实践中应避免直接操作它们,否则容易破坏切片内部结构。
十、向量(Vector)
一句话:向量是固定长度、同类型的集合,用于 SIMD(单指令多数据)并行计算,直接映射 CPU 硬件加速指令。
10.1 定义向量
使用 @Vector(N, T) 语法:
| |
10.2 向量运算
向量重载了算术、位运算和比较运算符,所有运算按元素独立执行,自动编译为 SIMD 指令:
| |
10.3 向量的广播(splat)
用 @splat 将一个标量扩展为向量:
| |
10.4 向量比较
比较结果是一个布尔向量:
| |
10.5 💡 使用建议
| 场景 | 是否适合用 Vector |
|---|---|
| 数值计算、图形处理、批量数据处理(利用 CPU SIMD 加速) | ✅ 适合 |
| 通用业务逻辑 | ❌ 不适合 |
| 长度不固定的数据(长度必须在编译期已知) | ❌ 不适合 |
| 需要运行时确定长度的批量数据 | 请使用切片(Slice) + 循环 |
十一、零大小类型(Zero-Sized Types)
一句话:某些类型在内存中不占用任何空间(0 bit) 。
11.1 void
void 是最典型的零大小类型,常用于表示“无返回值”:
| |
void 还可用于初始化泛型实例,例如构建一个集合(Set):
| |
11.2 其他零大小类型
| 类型 | 条件 |
|---|---|
u0 / i0 | 0 位整数 |
| 数组 / 切片 | 长度为 0,或元素类型是零大小类型 |
| 枚举 | 只有一个成员 |
| 结构体 | 空结构体,或所有字段都是零大小类型 |
| 联合类型 | 仅包含一种可能类型且该类型是零大小类型 |
| |
十二、类型转换
Zig 是强类型语言,不会自动进行隐式类型转换。你需要显式地进行转换。
12.1 基本转换:@as
| |
12.2 整数转换
| 内建函数 | 用途 |
|---|---|
@intCast | 在整数类型之间转换(可能截断或符号变化) |
@intFromFloat | 将浮点数转为整数(需配合 @floor/@ceil 等) |
@floatFromInt | 将整数转为浮点数 |
| |
12.3 指针转换
| 内建函数 | 用途 |
|---|---|
@ptrCast | 在不同指针类型之间转换 |
@alignCast | 改变指针的对齐方式 |
📌 总复习:核心要点速查
| 知识点 | 一句话总结 |
|---|---|
| 整数 | i8/u8/i32/u32/… 位宽明确;usize 用于索引 |
| 任意位宽整数 | i7、u12 等,最大 65535 位 |
| 整数溢出 | Debug/ReleaseSafe 会 panic;ReleaseFast/ReleaseSmall 是 UB |
| 浮点数 | f16/f32/f64/f128 |
| 布尔值 | bool = true / false,必须为 0 或 1 |
| 字符 | 没有 char 类型,用 'A' 表示 Unicode 码位(comptime_int) |
| 字符串 | 双引号 "Hello",类型 *const [N:0]u8(带哨兵),UTF-8 编码 |
| c_char | 仅在 C 交互时使用,符号性依赖目标平台 |
| 可选类型 | ?T = null 或 T,用 orelse 或 if 解构 |
| 错误联合 | !T = error 或 T,用 try 或 catch 处理 |
| 数组 | [N]T 固定长度,值类型(赋值会复制) |
| 切片 | []T 动态视图,“胖指针”([*]T + len) |
| 向量 | @Vector(N, T),SIMD 并行计算;长度必须编译期已知 |
| 零大小类型 | void、u0、空结构体等,占用 0 bit |
| 类型转换 | 必须显式转换,用 @as、@intCast 等 |
下一步建议
掌握基本类型后,建议继续学习:
- 复合类型进阶:结构体(
struct)、枚举(enum)、联合(union) - 指针:单项指针(
*T)、多项指针([*]T)、切片指针([]T) - 控制流:
if、while、for、switch - 内存管理:分配器(Allocator)、堆内存、栈内存
- 编译期编程:
comptime、泛型(anytype)
💡 调试技巧:如果不确定某个变量的类型,可以用
@compileLog(@TypeOf(x))在编译时打印类型信息(会中断编译,仅用于调试)。

