Zig 是一门现代化的系统级编程语言,由 Andrew Kelley 创建。它的设计理念强调显式优于隐式 、编译时代码执行 、零成本抽象 以及与 C 语言的完美互操作性。本文将全面介绍 Zig 的基础语法。
本文示例基于 Zig 0.13.x,个别在后续版本中更名的 API 会以注释标出。
变量与常量 var 与 const Zig 使用 var 声明可变变量,使用 const 声明常量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const std = @import ( "std" ) ; pub fn main ( ) void { var x: i32 = 10 ; x = 20 ; const y: i32 = 30 ; var z = 50 ; }
基本数据类型 Zig 的数值类型命名即位宽,规则统一、一目了然:
类别 类型 说明 有符号整数 i8 i16 i32 i64 i1288~128 位 无符号整数 u8 u16 u32 u64 u1288~128 位 任意位宽整数 i7 u1 u20Zig 特有,位宽可任意指定 指针大小整数 usize isize随平台指针位宽变化,常用于索引 浮点数 f16 f32 f64 f80 f128IEEE 754 布尔 bool只有 true / false 编译期数值 comptime_int comptime_float数值字面量的默认类型
1 2 3 4 5 const a: i32 = 100000 ; const b: u8 = 255 ; const pi: f64 = 3.14159265359 ; const flag: bool = true ; const ch: u21 = '中' ;
undefined:显式的未初始化 Zig 不允许「先声明、忘了初始化」,未初始化必须显式写出:
1 2 var x: i32 = undefined ; x = 10 ;
数组与切片 数组 数组是固定大小的同类型元素集合,长度是类型的一部分:
1 2 3 4 5 6 7 8 9 10 11 const arr = [ 5 ] i32 { 1 , 2 , 3 , 4 , 5 } ; const arr2 = [ _] i32 { 1 , 2 , 3 } ; const zeros = [ _] i32 { 0 } ** 10 ; const len = arr. len;
切片 切片是「指针 + 长度」的视图,是对一段连续内存的引用:
1 2 3 4 5 6 7 8 9 10 var arr = [ _] i32 { 1 , 2 , 3 , 4 , 5 } ; const slice = arr[ 1 . . 4 ] ; slice[ 0 ] = 100 ; const str: [ ] const u8 = "Hello, Zig!" ;
多维数组 1 2 3 4 5 const matrix = [ 3 ] [ 3 ] i32 { [ _] i32 { 1 , 2 , 3 } , [ _] i32 { 4 , 5 , 6 } , [ _] i32 { 7 , 8 , 9 } , } ;
指针 Zig 的指针按「指向多少个元素、是否可为空、是否有哨兵」严格区分:
语法 名称 说明 *T单项指针 指向单个值,不可为 null,不支持指针算术 [*]T多项指针 指向未知长度的连续内存,边界由调用者保证 []T切片 指针 + 长度,日常最常用 [*:0]T哨兵指针 以 0 结尾,兼容 C 字符串 [*c]TC 指针 与 C 指针完全等价,允许 null 和算术,仅用于 FFI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var x: i32 = 42 ; const ptr: * i32 = & x; ptr. * = 100 ; const maybe_ptr: ? * i32 = & x; var arr = [ _] i32 { 1 , 2 , 3 , 4 , 5 } ; const slice: [ ] i32 = & arr; const const_slice: [ ] const i32 = & arr; const str: [ : 0 ] const u8 = "hello" ;
与 C 互操作时会用到 [*c]T:
1 extern fn c_function ( ptr: [ * c] const u8 ) void ;
可选类型(Optionals) Zig 没有 null 引用,用 ?T 显式表达「可能没有值」:
1 2 3 4 5 6 7 8 9 10 11 12 13 const maybe_number: ? i32 = 42 ; const no_number: ? i32 = null ; if ( maybe_number) | n| { std. debug. print( "Number: {}\n" , . { n} ) ; } const value = maybe_number orelse 0 ; const definite = maybe_number. ? ;
可选类型还能配合 while 实现迭代器模式(iter 需实现返回 ?T 的 next 方法):
1 2 3 while ( iter. next( ) ) | value| { std. debug. print( "{} " , . { value} ) ; }
控制流 if / else if / else 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const x: i32 = 10 ; if ( x > 5 ) { std. debug. print( "x is greater than 5\n" , . { } ) ; } else if ( x == 5 ) { std. debug. print( "x equals 5\n" , . { } ) ; } else { std. debug. print( "x is less than 5\n" , . { } ) ; } const y = if ( x > 5 ) 100 else 0 ; const maybe_value: ? i32 = 42 ; if ( maybe_value) | value| { std. debug. print( "Value: {}\n" , . { value} ) ; } else { std. debug. print( "No value\n" , . { } ) ; }
while 与 for 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var i: i32 = 0 ; while ( i < 5 ) : ( i + = 1 ) { std. debug. print( "{} " , . { i} ) ; } var j: i32 = 0 ; while ( j < 10 ) : ( j + = 2 ) { if ( j == 4 ) continue ; std. debug. print( "{} " , . { j} ) ; } const arr = [ _] i32 { 1 , 2 , 3 , 4 , 5 } ; for ( arr) | elem| { std. debug. print( "{} " , . { elem} ) ; } for ( arr, 0 . . ) | elem, idx| { std. debug. print( "[{}] = {}\n" , . { idx, elem } ) ; } outer: for ( 0 . . 3 ) | a| { for ( 0 . . 3 ) | b| { if ( a + b == 3 ) break : outer; } }
switch:必须穷尽所有分支 Zig 的 switch 是详尽的(exhaustive),编译器会检查遗漏:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const number = 5 ; const result = switch ( number) { 1 = > "one" , 2 = > "two" , 3 = > "three" , 4 . . . 10 = > "between 4 and 10" , else = > "other" , } ; const Color = enum { red, green, blue } ; const color = Color. red; const color_name = switch ( color) { . red = > "Red" , . green = > "Green" , . blue = > "Blue" , } ;
函数 函数定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn add ( a: i32 , b: i32 ) i32 { return a + b; } fn printHello ( ) void { std. debug. print( "Hello!\n" , . { } ) ; } fn divide ( dividend: i32 , divisor: i32 ) struct { quotient: i32 , remainder: i32 } { return . { . quotient = @divTrunc ( dividend, divisor) , . remainder = @rem ( dividend, divisor) , } ; }
递归函数 1 2 3 4 fn factorial ( n: u32 ) u32 { if ( n <= 1 ) return 1 ; return n * factorial( n - 1 ) ; }
函数指针 1 2 3 4 5 6 7 8 9 10 11 12 const MathOp = fn ( a: i32 , b: i32 ) i32 ; fn multiply ( x: i32 , y: i32 ) i32 { return x * y; } fn calculate ( a: i32 , b: i32 , op: MathOp) i32 { return op( a, b) ; } const result = calculate( 5 , 3 , multiply) ;
结构体 基本结构体与方法 结构体通过「第一个参数为 self」的命名空间函数实现方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const Point = struct { x: f32 , y: f32 , pub fn distance ( self: Point, other: Point) f32 { const dx = self. x - other. x; const dy = self. y - other. y; return std. math. sqrt( dx * dx + dy * dy) ; } } ; const p1 = Point{ . x = 0 , . y = 0 } ; const p2 = Point{ . x = 3 , . y = 4 } ; const dist = p1. distance( p2) ;
默认字段值 1 2 3 4 5 6 7 8 const Config = struct { host: [ ] const u8 = "localhost" , port: u16 = 8080 , debug: bool = false , } ; const default_config = Config{ } ; const custom_config = Config{ . port = 3000 } ;
自引用结构体 1 2 3 4 const Node = struct { value: i32 , next: ? * Node = null , } ;
枚举与联合体 枚举(enum) 1 2 3 4 5 6 7 8 9 10 11 const Status = enum { pending, running, completed, failed, pub fn isDone ( self: Status) bool { return self == . completed or self == . failed; } } ;
联合体(union) 普通 union 不记录当前激活的字段,读取错误字段是未定义行为;union(enum) 是带标签的安全联合体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const Value = union { int: i32 , float: f64 , boolean: bool , } ; var v = Value{ . int = 42 } ; const TaggedValue = union( enum ) { int: i32 , float: f64 , boolean: bool , } ; const Message = union( enum ) { text: [ ] const u8 , number: i32 , quit: void , } ; fn handle ( msg: Message) void { switch ( msg) { . text = > | t| std. debug. print( "text: {s}\n" , . { t} ) , . number = > | n| std. debug. print( "number: {}\n" , . { n} ) , . quit = > std. debug. print( "quit\n" , . { } ) , } }
错误处理 Zig 没有异常,使用错误联合类型 (Error Union Type)让错误成为返回值类型的一部分。
定义错误集合 1 2 3 4 5 const FileError = error { NotFound, PermissionDenied, OutOfMemory, } ;
错误联合类型:!T FileError!i32 表示「要么返回 i32,要么返回 FileError 中的错误」:
1 2 3 4 5 6 fn mightFail ( condition: bool ) FileError! i32 { if ( ! condition) { return FileError. NotFound; } return 42 ; }
处理错误的几种方式 语法 作用 try expr出错则立即把错误返回给调用者 expr catch default出错时使用默认值 expr catch |err| {…}拿到错误值并处理 if (expr) |v| {…} else |err| {…}同时解构成功值与错误 errdefer {…}仅当函数以错误返回时执行清理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const value = try mightFail( true ) ; const value_or_default = mightFail( false ) catch 0 ; const value2 = mightFail( false ) catch | err| blk: { std. debug. print( "Got error: {}\n" , . { err} ) ; break : blk - 1 ; } ; if ( mightFail( true ) ) | value| { std. debug. print( "Success: {}\n" , . { value} ) ; } else | err| { std. debug. print( "Failed: {}\n" , . { err} ) ; }
errdefer 常用于资源清理——成功时不执行,失败时保证释放:
1 2 3 4 5 6 7 fn createResource ( ) ! * Resource { const res = try allocator. create( Resource) ; errdefer allocator. destroy( res) ; try res. init( ) ; return res; }
编译时计算(comptime) Zig 的强大特性之一:任何代码都可以在编译期执行。
comptime 变量与参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 comptime var count: i32 = 0 ; fn StaticArray ( comptime T: type , comptime size: usize ) type { return struct { data: [ size] T, pub fn get ( self: * @This ( ) , idx: usize ) T { return self. data[ idx] ; } } ; } const IntArray10 = StaticArray( i32 , 10 ) ; var my_array: IntArray10 = undefined ;
类型作为参数 类型在 Zig 中是一等值,泛型就是「接收类型、返回代码」:
1 2 3 4 5 6 fn max ( comptime T: type , a: T, b: T) T { return if ( a > b) a else b; } const m1 = max( i32 , 5 , 10 ) ; const m2 = max( f64 , 3.14 , 2.71 ) ;
编译期反射 1 2 3 4 5 6 7 8 9 10 11 12 13 fn printStructInfo ( comptime T: type ) void { const info = @typeInfo ( T) ; std. debug. print( "Type: {s}\n" , . { @typeName ( T) } ) ; if ( info == . Struct) { inline for ( info. Struct. fields) | field| { std. debug. print( " Field: {s} ({s})\n" , . { field. name, @typeName ( field. type ) , } ) ; } } }
泛型编程 Zig 没有专门的泛型语法,用「返回类型的函数」即可实现,下面是一个完整的泛型栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 fn Stack ( comptime T: type ) type { return struct { const Self = @This ( ) ; items: [ ] T, capacity: usize , len: usize = 0 , allocator: std. mem. Allocator, pub fn init ( allocator: std. mem. Allocator, capacity: usize ) ! Self { const items = try allocator. alloc( T, capacity) ; return Self{ . items = items, . capacity = capacity, . allocator = allocator, } ; } pub fn deinit ( self: * Self) void { self. allocator. free( self. items) ; } pub fn push ( self: * Self, item: T) ! void { if ( self. len >= self. capacity) return error.OutOfCapacity ; self. items[ self. len] = item; self. len + = 1 ; } pub fn pop ( self: * Self) ? T { if ( self. len == 0 ) return null ; self. len - = 1 ; return self. items[ self. len] ; } } ; } const IntStack = Stack( i32 ) ; var stack = try IntStack. init( allocator, 10 ) ; defer stack. deinit( ) ;
内存管理 Zig 不提供垃圾回收,所有分配都通过显式传入的 分配器(Allocator) 完成。
分配器模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const std = @import ( "std" ) ; pub fn main ( ) ! void { var gpa = std. heap. GeneralPurposeAllocator( . { } ) { } ; defer _ = gpa. deinit( ) ; const allocator = gpa. allocator( ) ; const arr = try allocator. alloc( i32 , 10 ) ; defer allocator. free( arr) ; for ( arr, 0 . . ) | * item, i| { item. * = @intCast ( i) ; } }
常用分配器对比 分配器 特点 适用场景 GeneralPurposeAllocator通用、线程安全、带泄漏检测 日常开发与调试 FixedBufferAllocator在固定缓冲区上分配,无堆分配 嵌入式、实时场景 ArenaAllocator只进不出,整体一次释放 请求/帧生命周期内的临时分配 page_allocator直接向 OS 申请内存页 大块分配、作为底层分配器
1 2 3 4 5 6 7 8 9 10 11 12 var buffer: [ 1024 ] u8 = undefined ; var fba = std. heap. FixedBufferAllocator. init( & buffer) ; const fba_allocator = fba. allocator( ) ; const slice = try fba_allocator. alloc( u8 , 100 ) ; var arena = std. heap. ArenaAllocator. init( std. heap. page_allocator) ; defer arena. deinit( ) ; const arena_allocator = arena. allocator( ) ; _ = try arena_allocator. alloc( u8 , 100 ) ; _ = try arena_allocator. alloc( u8 , 200 ) ;
与 C 语言互操作 Zig 可以无缝调用 C 代码,也可以被 C 调用。
导入 C 头文件 1 2 3 4 5 6 7 8 const c = @cImport ( { @cInclude ( "stdio.h" ) ; @cInclude ( "stdlib.h" ) ; } ) ; pub fn main ( ) void { _ = c. printf( "Hello from C!\n" ) ; }
导出 Zig 函数给 C 1 2 3 export fn zig_add ( a: i32 , b: i32 ) i32 { return a + b; }
类型映射 1 2 3 4 5 6 7 const c_int_val: c_int = 42 ; const c_char_val: c_char = 'a' ; const c_str: [ * c] const u8 = "hello" ; const zig_str: [ ] const u8 = std. mem. span( c_str) ;
构建系统 Zig 自带构建系统,构建脚本本身就是 Zig 代码(build.zig):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const std = @import ( "std" ) ; pub fn build ( b: * std. Build) void { const target = b. standardTargetOptions( . { } ) ; const optimize = b. standardOptimizeOption( . { } ) ; const exe = b. addExecutable( . { . name = "myapp" , . root_source_file = b. path( "src/main.zig" ) , . target = target, . optimize = optimize, } ) ; b. installArtifact( exe) ; const run_cmd = b. addRunArtifact( exe) ; run_cmd. step. dependOn( b. getInstallStep( ) ) ; const run_step = b. step( "run" , "Run the app" ) ; run_step. dependOn( & run_cmd. step) ; const unit_tests = b. addTest( . { . root_source_file = b. path( "src/main.zig" ) , . target = target, . optimize = optimize, } ) ; const run_unit_tests = b. addRunArtifact( unit_tests) ; const test_step = b. step( "test" , "Run unit tests" ) ; test_step. dependOn( & run_unit_tests. step) ; }
0.14 起 addExecutable 改用 .root_module = b.createModule(.{ ... }) 传入源文件与编译选项。
测试 Zig 内置测试框架,test 块就近写在源码里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const std = @import ( "std" ) ; const testing = std. testing; fn add ( a: i32 , b: i32 ) i32 { return a + b; } test "basic addition" { try testing. expect( add( 2 , 3 ) == 5 ) ; try testing. expectEqual( @as ( i32 , 5 ) , add( 2 , 3 ) ) ; } test "expect error" { const result = mightFail( false ) ; try testing. expectError( error.NotFound , result) ; }
运行测试:
包管理 Zig 使用 build.zig.zon 声明依赖:
1 2 3 4 5 6 7 8 9 10 . { . name = "myproject" , . version = "0.1.0" , . dependencies = . { . zap = . { . url = "https://github.com/zigzap/zap/archive/refs/tags/v0.1.0.tar.gz" , . hash = "1220..." , } , } , }
最佳实践 错误处理 :始终使用错误联合类型而非返回错误码内存管理 :使用 defer / errdefer 确保资源释放编译时计算 :利用 comptime 进行泛型和元编程显式控制 :避免隐式行为,所有操作都应清晰可见与 C 互操作 :用 Zig 逐步替换 C 代码,而不是一次性重写总结 Zig 是一门设计精良的系统编程语言,主要特点包括:
显式优于隐式 :没有隐藏的控制流或内存分配编译时代码执行 :强大的元编程能力零成本抽象 :高性能的同时保持代码清晰C 语言互操作 :无缝集成现有 C 代码安全第一 :通过编译时检查和显式错误处理避免运行时错误Zig 适合系统编程、嵌入式开发、游戏引擎、编译器等需要高性能和精细控制的场景。
参考资源