标题
zig 基本语法及基础功能使用方法[newbt的zig教程]
clq
浏览(543) +
2023-01-12 11:00:38 发表
编辑
关键字:
[2024-10-30 19:32:09 最后更新]
zig 基本语法及基础功能使用方法[newbt的zig教程] 这其实是一个 md 文档,在 github 中同步更新。地址为 https://github.com/clqsrc/sz_ui_align/blob/main/zig_file/zig%20%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95%E5%8F%8A%E5%9F%BA%E7%A1%80%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%5Bnewbt%E7%9A%84zig%E6%95%99%E7%A8%8B%5D.md 本文章原始地址为 http://newbt.net/ms/vdisk/show_bbs.php?id=9573656170788494499256AB77F15A09&pid=164 -------------------------------------------------------- zig 很小众。主要是语法变化目前还比较大(其实感觉上是标准库的位置和函数变化比较大)。 但用于某些环境中平替 c 语言是非常好的,特别是像 c++ 一样可以直接内嵌纯 c 语言,而不需要配置一个 c/c++ 编译器是我最接受它的原因。 不过目前已知的它并不能取代所有的 c/c++ 编译器,我所知道的 msys2 环境下编译 gtk 无法平替,其他的环境下基本上是完美。 参考 ``` https://github.com/msys2/MINGW-packages/issues/10596 https://github.com/msys2/MINGW-packages/pull/13274 https://github.com/ziglang/zig/issues/12985 ``` 编译为不同平台的 zig 的话似乎是一定要在 linux 下才行,msys2 下也是不行的,而且是用的另外一个项目 https://github.com/ziglang/zig-bootstrap 编译命令类似于 ``` ./build native-windows-gnu x86_64 ./build x86_64-windows-gnu x86_64 ``` 1. 平替 c 语言编译器的方法 很简单,将编译脚本中的 gcc 替换成 zig cc 即可。同理 g++ 替换成 zig c++ 即可。 这一知识的获取方法是 zig 的命令行帮助,命令好像是 zig -h 不太记得了。 1.1 推荐的其他中文 zig 教程: 1.1.1 https://zhuanlan.zhihu.com/p/459281215 yalight 东北大学秦皇岛分校 教师 这上面有的内容我基本上只转发到其他区,就不贴在这里了。 1.1.2 https://www.zhihu.com/question/50121841/answer/2655796837 其中的第二个回答 “你以为...” 系列很值得一看。 1.2 官方文档 https://ziglang.org/documentation/0.10.0/ 目前实际上好像就是一个页面而已,其实内容也比较全。 1.3 官方示例 https://ziglang.org/zh/learn/samples/ 2. 其他语言中常用功能、特性在 zig 中的实现方式。比较强制类型转换,还是很特别的。 2.1 强制类型转换 https://ziglang.org/documentation/0.10.0/#intCast 2.1.1 转换成 c 语言的整数类型。这在调用 c 函数时经常用,居然没看到有人说到。 ``` var b4 = args.len; //var b5 = @as(c_int, b4); //这个其实不是强制类型转换 //var i2 = @as(c_int, args.len); //奇怪,这句就不对 //https://ziglang.org/documentation/0.10.0/#Casting //test "cast *[1][*]const u8 to [*]const ?[*]const u8" { // const window_name = [1][*]const u8{"window name"}; // const x: [*]const ?[*]const u8 = &window_name; // try expect(mem.eql(u8, std.mem.sliceTo(@ptrCast([*:0]const u8, x[0].?), 0), "window name")); // } //var b5 = @ptrCast(c_int, b4); var b5 = @intCast(c_int, b4); ``` 我是看到了有 @ptrCast 的代码,然后自己试了好几种写法,果然有 @intCast 。 这在 zig 中并不是关键字,而是 https://ziglang.org/documentation/0.10.0/#Builtin-Functions 不知道有没有更好的更直接的强制转换方式。 ``` Builtin Functions Builtin functions are provided by the compiler and are prefixed with @. The comptime keyword on a parameter means that the parameter must be known at compile time. 内置功能 内置函数由编译器提供,前缀为@。参数上的comptime关键字表示该参数必须在编译时已知。 ``` 2.1.2 怎样转换出 c 语言常见的指针的指针。或者说是字符串数组。 ``` void function(const char **pNames) { *pNames = NULL; } ``` 官方文档也是没有的,感觉 zig 既然是以兼容 c 为最大卖点,接口的写法却着实难过。 最后的的解决办法直接来自于它的创始人作者。 https://github.com/ziglang/zig/issues/9479 ``` test "example" { var c_ptr: [*c][*c]const u8 = undefined; var zig_ptr: [*][*]const u8 = undefined; c_ptr = zig_ptr; } ``` 另外构建这样的字符串数组,还要借助 zig 中的数组类。 ``` const allocator2 = std.heap.c_allocator; var extensions = std.ArrayList([*]const u8).init(allocator2); errdefer extensions.deinit(); for (args) |arg, n| { //warn("arg{}: {}\n", n, arg); //std.log.info("arg{}: {}\n", n); //std.log.info("arg{}: {}\n", arg); std.log.info("numAddTen:{d}\n",.{n}); std.log.info("numAddTen:{s}\n",.{arg}); //var c_arg :[*]const u8 = arg; //try extensions.append(arg); //expected type '[*]const u8', found '[:0]u8' //try extensions.append(c_arg); //这个强制转换也很关键 try extensions.append(@ptrCast([*]const u8, &arg[0])); //extensions.appendSlice(arg); }// var c_argv = extensions.items.ptr; c_ptr = @ptrCast([*c][*c]u8, c_argv); //最后这个 c_ptr 才是对应到的 char ** argv 参数类型 _ = c._lua_main(c_argc, c_ptr); ``` 2.1.3 根据上面的例子就有会经常用到的 '[:0]u8' 转换为 '[*]const u8' 的方法 ``` @ptrCast([*]const u8, &arg[0])); ``` 不知道正宗的 zig 写法是什么,我这里是直接用了 delphi 中常用的取一个字符串原始地址的方法。 即取出一个字符串中第一个字符的地址就是整个内存块的起始地址。 注意并不是直接取字符串的地址。 这个方法成功后我也松了一口气,这样看来 zig 的内存低级操作还是有迹可循的,并没有太过份的语法糖魔术。 3. zig 自己的特性 和 c/c++/java 等传统语言不同的放这里介绍。其实有相当一部分新兴语言是有的,所以说是相当于是 zig 借鉴新兴语言的部分也可以。 3.1 defer 很好用的特性,和 golang 中的一模一样。就是在函数结束后要执行的清理工作代码。 3.2 与 c 语言字符串的接口。 3.2.1 导出函数给 c 语言用时。 直接看官方文档中的如下示例。这也是 zig 中访问 c 语言风格字符串的方法(c 语言字符串转换为 zig 的缓存片段类型)。 ``` 混合对象文件 您可以将 Zig 对象文件与遵循 C ABI 的任何其他对象文件混合使用。例: base64.zig const base64 = @import("std").base64; export fn decode_base_64( dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize, ) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; const base64_decoder = base64.standard.Decoder; const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable; base64_decoder.decode(dest[0..decoded_size], src) catch unreachable; return decoded_size; } ``` 999. zig 中的内存分配器 其实如果用过我们的 lstring 库就知道为什么要有个内存分配器。其实说白了就是一个方便批量释放内存的调用,就是你操作分配了一大堆内存后不记得每个内存块要在哪释放,不用操心。 在某个要退出操作的时候调用一个函数一次性释放就完了。 国内的 zig 教程也还是比较多的,不过似乎都没说到这个。所以重点提一下。 内存分配器可以自己创建也可以使用系统默认带有的,全部方式我也不太懂,目前已知的方式如下。 ``` //-------------------------------------------------------- //zig 取命令行参数的方法 //好像 golang 也是这样取的 //const args = try std.os.argsAlloc(allocator); //defer std.os.argsFree(allocator, args); //似乎已经过时了,在代码中 D:\no_install\zig-x86_64-0.10\lib\std\os.zig 中提示 // Populated by startup code before main(). // Not available on WASI or Windows without libc. See `std.process.argsAlloc` // or `std.process.argsWithAllocator` for a cross-platform alternative. //下面这段内存分配器的代码来自 D:\no_install\zig-x86_64-0.10\lib\std\testing.zig //而实际上 zig 是有默认的内存分配器的,而且似乎不止一个 //const allocator = std.mem.Allocator.allocator(); var b = std.heap.GeneralPurposeAllocator(.{}){}; var allocator_instance = b; const allocator1 = allocator_instance.allocator(); std.log.info("allocator1:{}\n",.{allocator1}); //这个默认的内存分配器示例来自 https://github.com/MasterQ32/zig-args/blob/master/demo.zig var allocator = std.heap.page_allocator; const args = try std.process.argsAlloc(allocator); //defer std.os.argsFree(allocator, args); defer allocator.free(args); //类似于 golang 的函数结束时的释放语句 //warn("total args: {}\n", args.len); for (args) |arg, n| { //warn("arg{}: {}\n", n, arg); //std.log.info("arg{}: {}\n", n); //std.log.info("arg{}: {}\n", arg); std.log.info("numAddTen:{d}\n",.{n}); std.log.info("numAddTen:{s}\n",.{arg}); }// std.log.info("命令行参数个数:{d}\n",.{args.len}); //-------------------------------------------------------- ``` 2024 更新,目前 zig 版本对参数传递给 c 语言 main 函数的方法又发生了变动,目前最新的如下(尚未同步 github) ``` //! By convention, root.zig is the root source file when making a library. If //! you are making an executable, the convention is to delete this file and //! start with main.zig instead. const std = @import("std"); const testing = std.testing; //----------------------------------------------- const qjs = @cImport({ //@cInclude("/fun3.c"); //这样居然也可以,不过尽量少用 //@cInclude("/fun2.h"); //这样也可以 //@cInclude("stdlib.h"); // 确保包含 C 标准库 //@cInclude("quickjs/libbf.c"); //似乎有很多和 quickjs.c 重复了 ////@cInclude("quickjs/quickjs.c"); //@cInclude("quickjs/qjsc.c"); //这样居然也可以,不过尽量少用 @cInclude("quickjs/qjsc.h"); //自己加的,只是为引入 //编译不了,还是直接用 c 编译吧 //zig cc qjsc.c quickjs.c libbf.c cutils.c libregexp.c libunicode.c quickjs-libc.c }); // qjsc 是命令行编译器: // ./qjsc -o hello examples/hello.js // ./hello // 生成一个没有外部依赖的 hello 可执行文件。 //----------------------------------------------- export fn add4(a: i32, b: i32) i32 { return a + b; } //不行,要加 pub 才能被其实文件访问。上面的 export 可能是库里面才能用的 //pub fn qjsc() error{OutOfMemory} { //pub fn qjsc() void { pub fn qjsc() !void { // 准备参数 // const args = [_][]const u8{ // "program_name", // "arg1", // "arg2", // }; // var arg_argc: c_int = @intCast(args.len); // //const arg_argv: [*c][*c]u8 = args; // const arg_argv: [*c][*c]u8 = undefined; // arg_argc = 0; //----------------------------------------------- // 使用传入的命令行参数 // 获取命令行参数 const c_alloc = std.heap.c_allocator; const allocator = std.heap.page_allocator; var arg_it = try std.process.argsWithAllocator(allocator); ////_ = arg_it.skip(); //const arg = arg_it.next() orelse return 200; //const arg = arg_it.next() orelse return undefined; var arg = arg_it.next(); //return try std.fmt.parseInt(usize, arg, 10); var arg_list = std.ArrayList([*]const u8).init(c_alloc); //----------------------------------------------- if (arg) |value| { //std.debug.print("Next argument: {}\n", .{value}); //这个是不行的,原因在于后面的值是字符串,要用 {s} 代替 {} ////std.debug.print("Next argument: {s}\n", .{value}); //这样才行 var c_ptr: [*c]const u8 = undefined; //const zig_ptr: [*]const u8 = &value[0]; //undefined; const zig_ptr = &value[0]; //undefined; //c_ptr = zig_ptr; c_ptr = @ptrCast(zig_ptr); //arg_list.append(value); try arg_list.append(c_ptr); } else { return; // 没有更多参数,退出循环 } //----------------------------------------------- //var arg_list = std.ArrayList([*]const u8).init(c_alloc); // 打印命令行参数 //for (args) |arg| { while (true) { arg = arg_it.next(); //----------------------------------------------- if (arg) |value| { //std.debug.print("Next argument: {}\n", .{value}); var c_ptr: [*c]const u8 = undefined; //const zig_ptr: [*]const u8 = &value[0]; //undefined; const zig_ptr = &value[0]; //undefined; //c_ptr = zig_ptr; c_ptr = @ptrCast(zig_ptr); //arg_list.append(value); try arg_list.append(c_ptr); } else { //return; // 没有更多参数,退出循环 break; } //if } //while //----------------------------------------------- //-- 手工加一个参数 //ok 成功,至少在当前版本下 if (false) { const value2 = "12345"; var c_ptr2: [*c]const u8 = undefined; const zig_ptr2 = &value2[0]; c_ptr2 = @ptrCast(zig_ptr2); try arg_list.append(c_ptr2); } //if //----------------------------------------------- // 创建一个指向指针的指针 var c_ptr: [*c][*c]const u8 = undefined; const zig_ptr: [*][*]const u8 = arg_list.items.ptr; //undefined; const arg_argc: c_int = @intCast(arg_list.items.len); //c_ptr = zig_ptr; c_ptr = @ptrCast(zig_ptr); //----------------------------------------------- //qjs.main(); //这个 c 文件里的 main 要改 //qjs.main_c(0, qjs.NULL); //qjs.free1(0); //_ = qjs.main_c(arg_argc, arg_argv); //在 qjsc.c 文件中 //_ = qjs.main_c(arg_argc, &arg_argv_dynamic[0]); //在 qjsc.c 文件中 _ = qjs.main_c(arg_argc, c_ptr); //在 qjsc.c 文件中 //return a + b + a2; } test "basic add functionality" { try testing.expect(add4(3, 7) == 10); } ```
NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.