我想编写一个函数来读取文本文件,并将其行作为字符串数组返回以供进一步处理。
为此,我试图正确处理 N 长度的字符串数组,其中 N 在运行时确定。
我有一个基本示例,其中包含长度为 2 的硬编码数组。它运行良好,并且没有报告任何泄漏:
const std = @import("std");
const stdout = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
pub fn main() !void {
const alloc = gpa.allocator();
defer _ = gpa.deinit();
const names = try getNames(alloc);
defer destroyNames(alloc, names);
for(names) |name| {
try stdout.print("Hello {s}\n", .{name});
}
}
fn destroyNames(alloc:std.mem.Allocator, names:*[2][]const u8) void {
for(names) |name| {
// Correctly frees the '[]const u8'
alloc.free(name);
}
_ = alloc.destroy(names);
}
fn getNames(alloc:std.mem.Allocator) !*[2][]const u8 {
const names = [_][]const u8{"Alix", "Bub"};
const new_names = try alloc.create([2][]const u8);
const title = "Prof";
for(names, 0..) |name, i| {
new_names[i] = try std.fmt.allocPrint(alloc, "{s} {s}", .{title, name} );
}
return new_names;
}
对此感到满意,我开始移动一些东西。以下运行,但在延迟
destroyNames
操作 上失败
const std = @import("std");
const stdout = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
pub fn main() !void {
const alloc = gpa.allocator();
defer _ = gpa.deinit();
const names = try getNames(alloc);
defer destroyNames(alloc, names);
for(names.*) |name| {
try stdout.print("Hello {s}\n", .{name});
}
}
fn destroyNames(alloc:std.mem.Allocator, names:*[][]const u8) void {
for(names.*) |name| {
// ERROR - core dumps here...!
// Cannot properly free a '[]const u8' supplied like this
alloc.free(name);
}
_ = alloc.destroy(names);
}
fn getNames(alloc:std.mem.Allocator) !*[][]const u8 {
// Stand-in. Hypothetically, read from a N-line file...
const names = [_][]const u8{"Alix", "Bub"};
var new_names:[][]const u8 = undefined;
new_names = try alloc.create([names.len][]const u8);
const title = "Prof";
for(names, 0..) |name, i| {
new_names[i] = try std.fmt.allocPrint(alloc, "{s} {s}", .{title, name} );
}
return &new_names;
}
运行结果
$ zig run length-general.zig
Hello Prof Alix
Hello Prof Bub
General protection exception (no address available)
/home/tai/.local/var/zig/zig-linux-x86_64-0.14.0-dev.1366+d997ddaa1/lib/compiler_rt/memset.zig:19:14: 0x10f7f10 in memset (compiler_rt)
d[0] = c;
^
/home/tai/.local/var/zig/zig-linux-x86_64-0.14.0-dev.1366+d997ddaa1/lib/std/mem/Allocator.zig:313:26: 0x1040e57 in free__anon_3449 (length-general)
@memset(non_const_ptr[0..bytes_len], undefined);
^
/home/tai/scratch-zig/length-general.zig:26:19: 0x103bacc in destroyNames (length-general)
alloc.free(name);
^
/home/tai/scratch-zig/length-general.zig:14:23: 0x103b59c in main (length-general)
defer destroyNames(alloc, names);
^
/home/tai/.local/var/zig/zig-linux-x86_64-0.14.0-dev.1366+d997ddaa1/lib/std/start.zig:615:37: 0x103ad2f in posixCallMainAndExit (length-general)
const result = root.main() catch |err| {
^
/home/tai/.local/var/zig/zig-linux-x86_64-0.14.0-dev.1366+d997ddaa1/lib/std/start.zig:250:5: 0x103a90f in _start (length-general)
asm volatile (switch (native_arch) {
^
???:?:?: 0x0 in ??? (???)
Aborted (core dumped)
作为尝试检查,我确实尝试在
两个示例中
alloc.free(name.*)
,并且两个相同都报告了error: index syntax required for slice type '[]const u8'
,这对我来说意味着他们都相同确实收到了[]const u8
如何修复这些错误?
在修改版本中,函数 getNames 返回一个指向存储在堆栈上的值的指针,这是不允许的。
在 zig 中,数组类型有些令人困惑:
[N]u8
是一个“数组”,一个值类型[]u8
是一个“切片”,一个指针类型。类似于struct {ptr: [*]u8, len: usize}
[*]u8
是一个指针,类似于 *u8
但它允许数组索引语法。类型
*[2]u8
可以隐式转换为切片 []u8
。然后,您将返回 &new_names
,它获取指向堆栈值的指针。从函数返回后,堆栈中的数据会被覆盖,然后一切都会变得混乱。
解决方案是返回
[][]const u8
而不是 *[][]const u8
。
还有一点需要注意:
try alloc.create([names.len][]const u8);
数组类型的长度必须在编译时已知 - 所以此调用仅有效,因为
names.len
在编译时已知为 2。如果您从文件中读取,则不会知道编译时的长度,这不会不工作。您可能想在此处使用 alloc.alloc()
来分配切片:
try alloc.alloc(u8, names.len);
和
alloc.free()
释放它
另一件事:
var new_names:[][]const u8 = undefined;
new_names = ...;
没有理由将变量初始化为未定义,然后立即在下一行填充。
const new_names = ...;