在日常的 C++ 后台开发工作中,很少会动态生成 Zip 包,所以对 C++ 的 libzip 并不熟悉。最近刚好有个场景,需要将后台生成的一份数据压缩为一个 Zip 包以便下载。这里其实之前已经有生成 Zip 包的代码,只是需要在 Zip 包里面增加一个文件。本来是一个简单的需求,但是实现中遇到了一个诡异的问题,解压生成的 Zip 包里,里面文件开头部分有错乱。
C++ 内存问题排查:创建 Zip 压缩包,解压后内容错乱
包含完整的:

问题复现
问题排查

GDB 验证
添加调试符号
定位读内存位置

总结

Why not using tar ball, it is simpler than zip.

之前历史协议就是定的 zip ,改的话需要很多地方适配

是不是字符编码问题导致的呢?纯英文会不会有问题?

省流:foreach 遍历时没加引用,把数据拷了一份,然后添加到 libzip 的结构中,循环过后数据被释放,但是 libzip 之后还会读这些数据,于是就出问题了。

哈哈哈,是够省流。

刚开始还真怀疑是编码问题,不过很容易就排除了。可以看下帖子提到的文章,里面有全部分析过程

试过 address sanitizer 吗,似乎是典型场景

之前给 libzip 做过 jni 封装,给 source 添加数组数据时自己 malloc 一次(然后不管它,zip_close 时会释放 source ,source 释放时又会释放内部 in/out)。

多写点 rust 你会对接口的生命周期要求很敏感。

嗯,应该也能分析出来。不过我没试

libzip 的接口文档也写的不咋地

rust 没用过,可以详细说下为啥会对接口生命周期要求敏感?

其实可以用 libarchive 嘛,连微软都用最起码的,它的文档够清晰

libarchive 感觉太大了,支持一堆格式,能配置只支持某种格式吗?

因为 rust 可以说就是为了解决生命周期问题而生的。。

#12 你去试试就知道了,rust 对 cpp 程序员完全是无痛的。rust 的引用把生命周期包含在类型系统当中,你遇到的这个问题放 rust 里就是编译时错误,rust 写多了再用回裸指针没了编译器保护反而也会格外关注生命周期的正确性。

#12 Rust 相当于在编译期做内存管理,你这个 case 会编译不过

那就要自己裁剪了,没办法配置成只支持某种格式libarchive 原本只是 FreeBSD 的内部库,只不过开发团队单独提取出来给大家用,这么来看的话不太可能提供靠配置选格式支持的功能我在 FreeBSD 编译过使用 libarchive 的程序,发现这个库确实是内置的Windows 的话,我发现自 2023 年 9 月更新后,Win11 内置了 archiveint.dll ,位置是 C:\Windows\System32\archiveint.dll ,或许链接到这个 DLL 有助于降低 exe 的大小(具体能不能我也不知道,还没试)

不考虑链接 Windows 自带的 dll ,因为要支持老系统

编译期内存管理,有点厉害。那内存泄露啥的也不会有了,怪不得一个哥们说 rust 还挺好用的

嗯,感谢推荐,文档清晰太重要了

使用不当的话,内存泄漏还是有机会出现的: doc.rust-lang.org/book/ch15-06-reference-cycles.html stackoverflow.com/questions/55553048/is-it-possible-to-cause-a-memory-leak-in-rust