我想编写一个函数来读取文本文件,并将其行作为字符串数组返回以供进一步处理。
为此,我试图正确处理长度为 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
。另外要注意的是:
数组类型的长度必须在 comptime 时已知 - 因此此调用仅
names.len
在 comptime 时已知为 2 时才有效。如果您正在从文件中读取,则在 comptime 时您不知道长度,因此此调用将不起作用。您可能希望在此处使用alloc.alloc()
来分配切片:并
alloc.free()
释放它另一件事:
没有理由将一个变量初始化为未定义,然后立即在下一行进行填充。