无法从任意长度的字符串数组中获取无内存字符串

问题描述 投票:0回答:1

我想编写一个函数来读取文本文件,并将其行作为字符串数组返回以供进一步处理。

为此,我试图正确处理 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

如何修复这些错误?

arrays string heap zig
1个回答
0
投票

在修改版本中,函数 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 = ...;
© www.soinside.com 2019 - 2024. All rights reserved.