gzip解压必须用inflateInit2(&strm, 16 + MAX_WBITS),因gzip含10字节头和8字节尾(含CRC),zlib默认只认raw deflate流;需循环调用inflate直至返回Z_STREAM_END,并在结束后调用inflateEnd清理资源。

gzip解压必须用 inflateInit2,不能用 inflateInit
直接调 inflateInit 会失败,返回 Z_DATA_ERROR。因为 gzip 格式带 10 字节头部和 8 字节尾部(含 CRC),zlib 默认只认 raw deflate 流。必须用 inflateInit2(&strm, 16 + MAX_WBITS) —— 这里的 16 是关键,它告诉 zlib “预期输入是 gzip 封装格式”,MAX_WBITS(通常是 15)控制窗口大小。
常见错误现象:inflate 第一次调用就返回 Z_DATA_ERROR,或解出来全是乱码;根本没报错但输出长度为 0。
-
16 + MAX_WBITS是硬编码值,不是宏拼接,直接写16 + 15也行(但建议用MAX_WBITS保证可移植) - 如果数据其实是 zlib 格式(无头尾),反而要传
-MAX_WBITS;gzip 和 zlib 封装不能混用 - 初始化失败时检查
strm.msg,比如"invalid window size"说明wbits值错了
解压过程要循环调用 inflate,不能只调一次
inflate 是流式接口,一次最多处理输入缓冲区里的数据,不会自动读完全部。尤其当输出缓冲区满、或输入暂无新数据时,它会返回 Z_OK 并停住,等你换缓冲区再继续。
使用场景:内存受限时分块解压;或输入数据来自网络/文件流,无法一次性全读入。
立即学习“C++免费学习笔记(深入)”;
- 每次调
inflate前确保strm.avail_in > 0,否则会卡在Z_BUF_ERROR - 输出缓冲区填满后,先保存已解出的数据,再重置
strm.next_out和strm.avail_out,再继续调用 - 循环结束标志是
inflate返回Z_STREAM_END;如果中途返回其他非Z_OK值(如Z_DATA_ERROR),说明数据损坏或格式不匹配
inflateEnd 必须在解压完成后调用,且只能调一次
不调 inflateEnd 会导致 zlib 内部分配的内存泄漏;重复调用会触发断言失败(debug 版)或未定义行为(release 版)。
性能影响:虽然单次调用开销极小,但在高频短解压场景(如 HTTP 响应体解压),漏掉它会让进程 RSS 持续上涨。
- 务必放在所有
inflate调用之后,且只在确认返回Z_STREAM_END或明确失败后调用 - 如果解压中途出错(比如
Z_MEM_ERROR),也要调inflateEnd清理资源 - C++ 中建议用 RAII 封装,比如局部
z_stream配合 lambda 或自定义析构,避免手滑遗漏
输入数据必须完整,且不能带多余字节
gzip 是严格封装格式,zlib 会校验尾部 CRC 和 ISIZE(原始未压缩长度)。如果输入 buffer 多了几个字节(比如把两个 gzip 包拼在一起),inflate 在最后一步会返回 Z_DATA_ERROR,即使前面解出来的数据看起来“对”。
容易踩的坑:从 socket 读数据时没按帧分离;用 fread 读文件但没检查实际读取字节数;Base64 解码后忘了去掉填充字符导致末尾多出 =。
- 用
gunzip -t xxx.gz先验证原始数据是否合法 - 如果确定数据来自 HTTP,注意
Content-Encoding: gzip的 body 就是纯 gzip 流,不要额外加 HTTP 头或分块编码标记 - 调试时打印
strm.total_in和输入 buffer 长度,两者必须相等才代表 zlib 真的消费完了全部输入
最常被忽略的是 wbits 参数的符号和偏移组合,以及解压循环中对 Z_STREAM_END 的判断位置——很多人在第一次 inflate 返回 Z_OK 就以为结束了,其实后面还有数据没吐出来。


















