Zig中ArrayList动态分配条目内存泄漏问题修复求助
修复Zig UTF-8字符串分割函数的内存泄漏与崩溃问题
问题根源
你的代码存在三个核心问题:
- 初始追加的空字符串是静态常量
"",后续执行allocator.free(last)时尝试释放静态内存,触发未定义行为(崩溃或内存错误)。 std.mem.concat的错误处理逻辑错误,直接返回字符串字面量不符合函数的返回类型要求。- 测试代码仅调用
actual.deinit()不会释放ArrayList中每个元素的内存,导致内存泄漏。
修复后的函数代码
const std = @import("std"); fn split_utf8(allocator: std.mem.Allocator, in: []const u8, delimiter: []const u8) !std.ArrayList([]const u8) { var list = std.ArrayList([]const u8).init(allocator); // 分配堆内存的空字符串,确保后续可安全释放 try list.append(try allocator.dupe(u8, "")); var utf8 = (try std.unicode.Utf8View.init(in)).iterator(); while (utf8.nextCodepointSlice()) |codepoint| { if (std.mem.eql(u8, codepoint, delimiter)) { // 同样分配堆内存的空字符串作为新条目初始值 try list.append(try allocator.dupe(u8, "")); continue; } const last = list.pop(); defer allocator.free(last); // 正确传播concat的错误,而非返回无效的字符串字面量 const new = try std.mem.concat(allocator, u8, &[_][]const u8{last, codepoint}); try list.append(new); } return list; }
修复后的测试代码
test "Split test" { const testing = std.testing; var expected = std.ArrayList([]const u8).init(testing.allocator); defer expected.deinit(); try expected.append("F"); try expected.append("1"); try expected.append("100"); try expected.append("📷click"); const actual = try split_utf8(testing.allocator, "F 1 100 📷click", " "); defer { // 先释放每个元素的堆内存,再释放ArrayList自身结构 for (actual.items) |item| { testing.allocator.free(item); } actual.deinit(); } try testing.expectEqual(expected.items.len, actual.items.len); // 验证每个分割结果的内容一致性 for (expected.items, actual.items) |exp, act| { try testing.expectEqualStrings(exp, act); } }
关键修复点说明
- 统一堆分配元素:所有列表条目都使用
allocator.dupe分配堆内存,避免静态内存与堆内存混合导致的释放错误。 - 正确错误传播:移除错误分支的无效字符串返回,使用
try传递std.mem.concat的原生错误,符合函数的返回类型约定。 - 完整内存释放:测试代码中先遍历释放每个元素的内存,再调用
deinit释放ArrayList自身的结构内存,彻底解决泄漏问题。
优化方案(减少内存分配次数)
上述修复版本每次拼接都重新分配内存,效率较低。可以改用std.ArrayList(u8)动态构建每个条目,减少分配次数:
fn split_utf8(allocator: std.mem.Allocator, in: []const u8, delimiter: []const u8) !std.ArrayList([]const u8) { var list = std.ArrayList([]const u8).init(allocator); var current = std.ArrayList(u8).init(allocator); defer current.deinit(); var utf8 = (try std.unicode.Utf8View.init(in)).iterator(); while (utf8.nextCodepointSlice()) |codepoint| { if (std.mem.eql(u8, codepoint, delimiter)) { // 将当前构建的字符串转为堆分配切片,加入结果列表 try list.append(try current.toOwnedSlice()); current.clearRetainingCapacity(); continue; } try current.appendSlice(codepoint); } // 加入最后一个未完成的条目 try list.append(try current.toOwnedSlice()); return list; }
这个版本用动态数组逐字符构建条目,避免了多次拼接分配,内存效率更高,释放逻辑与之前一致。
内容的提问来源于stack exchange,提问作者lukstru
相关产品推荐
相关产品推荐

