Skip to content

Zig 动态库教程

动态库(Dynamic Library,也称共享库)是在运行时加载的库文件,允许多个程序共享相同的代码段,减少内存占用。

主要特点:

  • 运行时加载:程序启动或运行时加载
  • 代码共享:多个进程可共享同一份库代码
  • 独立更新:可单独更新库而不重新编译程序
  • 符号解析:运行时解析函数地址
// fizzbuzz.zig - 动态库源码
const std = @import("std");
// 导出函数:会被其他程序调用
export fn fizzbuzz(n: u32) bool {
return n % 3 == 0 or n % 5 == 0;
}
// 内部函数:不会被导出
fn internal_helper() void {
// 内部实现细节
}
// 导出带 C ABI 的函数
export fn fibonacci(n: u32) u32 {
if (n < 2) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// build.zig - 动态库构建脚本
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// 创建动态库
const libfizzbuzz = b.addLibrary(.{
.name = "fizzbuzz", // 库名称
.linkage = .dynamic, // 关键:设置为动态链接
.version = .{ // 版本号
.major = 1,
.minor = 2,
.patch = 3,
},
.root_module = b.createModule(.{
.root_source_file = b.path("fizzbuzz.zig"),
.target = target,
.optimize = optimize,
}),
});
// 可选:设置 soname(ELF 系统中)
libfizzbuzz.link_system_libc = false;
libfizzbuzz.install();
}

运行 zig build 后生成:

zig-out/lib/
├── libfizzbuzz.so -> libfizzbuzz.so.1 # 主符号链接
├── libfizzbuzz.so.1 -> libfizzbuzz.so.1.2.3 # 主版本号链接
└── libfizzbuzz.so.1.2.3 # 实际库文件
// main.zig - 使用动态库的程序
const std = @import("std");
// 声明外部函数(来自动态库)
extern fn fizzbuzz(n: u32) bool;
extern fn fibonacci(n: u32) u32;
pub fn main() void {
// 调用动态库中的函数
std.debug.print("测试 FizzBuzz:\n", .{});
for (1..21) |n| {
if (fizzbuzz(@intCast(n))) {
std.debug.print("{}, ", .{n});
}
}
std.debug.print("\n", .{});
// 调用另一个函数
const fib_10 = fibonacci(10);
std.debug.print("fibonacci(10) = {}\n", .{fib_10});
}

3.2 构建使用动态库的可执行文件

Section titled “3.2 构建使用动态库的可执行文件”
// build.zig - 链接动态库的程序
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// 创建可执行文件
const exe = b.addExecutable(.{
.name = "fizzbuzz_app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// 链接动态库
exe.linkSystemLibrary("fizzbuzz");
// 设置库搜索路径(如果不在标准路径)
exe.addLibraryPath(b.path("zig-out/lib"));
// 运行时库搜索路径(可选)
if (target.result.os.tag == .linux) {
exe.addRPath(b.path("zig-out/lib"));
}
b.installArtifact(exe);
}
// complex.zig - 导出复杂类型
const std = @import("std");
// 导出结构体(需要 C 兼容布局)
pub const Complex = extern struct {
real: f64,
imag: f64,
pub fn init(real: f64, imag: f64) Complex {
return Complex{ .real = real, .imag = imag };
}
pub fn add(self: Complex, other: Complex) Complex {
return Complex{
.real = self.real + other.real,
.imag = self.imag + other.imag,
};
}
};
// 导出使用结构体的函数
export fn complex_add(a: Complex, b: Complex) Complex {
return a.add(b);
}
// 使用版本控制来管理 ABI 兼容性
pub const fizzbuzz_v1 = struct {
pub export fn fizzbuzz(n: u32) bool {
return n % 3 == 0 or n % 5 == 0;
}
};
pub const fizzbuzz_v2 = struct {
// 新版本添加额外功能
pub export fn fizzbuzz_ext(n: u32) struct {
is_fizz: bool,
is_buzz: bool
} {
return .{
.is_fizz = n % 3 == 0,
.is_buzz = n % 5 == 0,
};
}
};
// 控制符号可见性
comptime {
@export(fizzbuzz_v1.fizzbuzz, .{ .name = "fizzbuzz" });
@export(fizzbuzz_v2.fizzbuzz_ext, .{
.name = "fizzbuzz_ext",
.linkage = .Strong, // 强符号
});
}
// 库初始化函数(构造函数)
export fn __attribute__((constructor)) init_library() void {
std.debug.print("动态库初始化\n", .{});
// 初始化全局状态、分配资源等
}
// 库清理函数(析构函数)
export fn __attribute__((destructor)) cleanup_library() void {
std.debug.print("动态库清理\n", .{});
// 释放资源、清理状态
}
// 线程本地存储初始化
threadlocal var thread_counter: u32 = 0;
export fn get_thread_counter() u32 {
return thread_counter;
}
export fn increment_thread_counter() void {
thread_counter += 1;
}
// dynamic_loader.zig - 运行时加载动态库
const std = @import("std");
pub fn main() !void {
// 加载动态库
const lib = try std.DynLib.open("libfizzbuzz.so");
defer lib.close();
// 获取函数指针
const fizzbuzz_fn = try lib.lookup(fn (u32) bool, "fizzbuzz");
const fibonacci_fn = try lib.lookup(fn (u32) u32, "fibonacci");
// 调用函数
const result1 = fizzbuzz_fn(15);
const result2 = fibonacci_fn(10);
std.debug.print("fizzbuzz(15) = {}, fibonacci(10) = {}\n", .{
result1, result2
});
}
const Platform = struct {
const DynLib = switch (@import("builtin").os.tag) {
.windows => struct {
handle: std.os.windows.HMODULE,
pub fn open(path: []const u8) !@This() {
const handle = std.os.windows.LoadLibraryA(path.ptr);
if (handle == null) return error.LoadFailed;
return @This(){ .handle = handle.? };
}
pub fn lookup(self: @This(), comptime T: type, name: []const u8) !T {
const addr = std.os.windows.GetProcAddress(self.handle, name.ptr);
if (addr == null) return error.SymbolNotFound;
return @ptrCast(addr.?);
}
},
else => struct { // Unix-like
handle: *anyopaque,
pub fn open(path: []const u8) !@This() {
const handle = std.c.dlopen(path.ptr, std.c.RTLD_LAZY);
if (handle == null) return error.LoadFailed;
return @This(){ .handle = handle.? };
}
pub fn lookup(self: @This(), comptime T: type, name: []const u8) !T {
const addr = std.c.dlsym(self.handle, name.ptr);
if (addr == null) return error.SymbolNotFound;
return @ptrCast(addr.?);
}
},
};
};
// ziglib.zig - 供 C 调用的动态库
const std = @import("std");
// 导出 C 兼容函数
export fn add_numbers(a: c_int, b: c_int) c_int {
return a + b;
}
export fn reverse_string(str: [*:0]const u8) [*:0]u8 {
const len = std.mem.len(str);
const result = std.heap.c_allocator.alloc(u8, len + 1) catch
return null;
for (0..len) |i| {
result[i] = str[len - i - 1];
}
result[len] = 0;
return @ptrCast(result.ptr);
}
export fn free_string(ptr: [*:0]u8) void {
const len = std.mem.len(ptr);
const slice = ptr[0..len:0];
std.heap.c_allocator.free(slice);
}

C 语言调用代码:

main.c
#include <stdio.h>
#include <stdlib.h>
// 声明 Zig 函数
extern int add_numbers(int a, int b);
extern char* reverse_string(const char* str);
extern void free_string(char* ptr);
int main() {
// 调用加法函数
int sum = add_numbers(10, 20);
printf("Sum: %d\n", sum);
// 调用字符串反转函数
char* reversed = reverse_string("Hello World");
printf("Reversed