Ziglings 笔记 94: 借用 C 的数学库
站在巨人的肩膀上
在 Ziglings 的第 94 个练习中,我们继续探索 C Interop (C 互操作性)。 这一次,我们不再只是打印 Hello World,而是要解决一个实际的数学问题:浮点数取模。
虽然 Zig 提供了 @mod 内置函数,但为了演示目的,我们假设必须使用 C 语言标准库 math.h 中的 fmod 函数。这模拟了在实际项目中,我们不得不调用那些尚未被 Zig 社区重写的 C 库的场景。
挑战:角度归一化
我们有一个角度 765.2 度。我们需要把它归一化到 0-360 度之间。
这就需要用到浮点数取模运算:765.2 % 360。
我们将调用 C 语言的 fmod 函数来完成计算。
解决方案
我们需要在 @cImport 块中包含正确的头文件:
const std = @import("std");
// 引入 C 库
const c = @cImport({
// 引入 math.h 以使用 fmod 函数
@cInclude("math.h");
});
pub fn main() !void {
const angle = 765.2;
const circle = 360;
// 直接调用 C 函数
// fmod(x, y) 返回 x 除以 y 的浮点余数
// 765.2 - (2 * 360) = 45.2
const result = c.fmod(angle, circle);
// 格式化输出
// {d: >3.1} 意思是以十进制打印,右对齐宽度 3,保留 1 位小数
std.debug.print("The normalized angle of {d: >3.1} degrees is {d: >3.1} degrees.\n", .{ angle, result });
}
核心知识点总结
1. @cInclude 是如何工作的?
当你写 @cInclude("math.h") 时,Zig 编译器(利用内置的 Clang)会去系统的标准包含路径下寻找这个文件。它会解析文件中的函数声明、结构体定义和宏,并将它们暴露在 c 这个命名空间下。
2. 跨语言的类型桥接
C 语言的 fmod 接收两个 double。
Zig 并没有强迫我们显式地把参数转为 f64。因为 Zig 的数值字面量是无类型的(comptime_float),编译器会自动将它们“适配”进 C 函数的参数类型中。这种体验非常顺滑。
3. 为什么是 -lc?
虽然代码里写了 @import,但在运行这个程序时,我们必须告诉编译器去连接 C 的运行时库。
命令:zig run -lc main.zig。
如果不加 -lc,链接器会报错,因为它找不到 fmod 的具体实现代码(机器码)。
后续预告:我们已经成功调用了 C 函数。但是,C 语言的生态中充满了各种复杂的宏定义。Zig 能理解那些宏吗?下一篇,我们将看看 Zig 如何处理 C Macros。