如何使用 zlib 正确打开和关闭已经打开的 gzip 文件?

How to properly open and close an already fopened gzip file with zlib?

我正在编写一个小型 C 库,用于读取作为 FILE * 传递的 gzip 文件。我正在使用 zlib 的 gzdopen() 打开文件文件描述符:

int zOpenCloseTest(FILE *const plainFile) {
    gzFile file = gzdopen(fileno(plainFile), "rb");
    if(file == NULL) {
        goto error;
    }
    if(gzclose_r(file) != Z_OK) {
        goto error;
    }
    return 0;
    error:
    // gzdopen does not close fd if it fails
    fclose(plainFile);
    return -1;
}

int main() {
    FILE *const file = fopen("test.xp", "rb");
    if(file == NULL) {
        return -1;
    }
    if(zOpenCloseTest(file) < 0) {
        return -2;
    }
    return 0;
}

zlib manual 状态:

File descriptors are obtained from calls like open, dup, creat, pipe or fileno (in the file has been previously opened with fopen). [...] The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd), mode) closes the file descriptor fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, mode);. [...] If you are using fileno() to get the file descriptor from a FILE *, then you will have to use dup() to avoid double-close()ing the file descriptor. Both gzclose() and fclose() will close the associated file descriptor, so they need to have different file descriptors.

我不想让文件保持打开状态,所以我不创建重复文件。

当使用 Valgrind memcheck (--leak-check=full --show-leak-kinds=all --track-origins=yes --vgdb=no --track-fds=yes) 检查时,上述代码在第 21 行 (fopen) 上产生 Leak_StillReachable 错误。
在 windows,它崩溃并显示“调试断言失败!” (_osfile(fh) & FOPEN) close.cpp line 49 错误框。

两者都是在“调试模式”下构建的。

该库是一个更大的 CMake 项目的一部分,用户可以选择将其构建为静态库还是共享库。无论哪种情况,我都希望将 zlib 静态 linked 到库中。 我最初认为崩溃是由于库和 zlib 之间的 运行-time 库 link 模式不匹配造成的,但最小的例子表明情况并非如此? 如果您检查 CMake 输出,您可以看到 zlib 正在使用 /MDd 标志构建,这是应该的。 (现代)CMake 通常默认为 CMAKE_MSVC_RUNTIME_LIBRARY.

的“MultiThreaded$<$CONFIG:Debug:Debug>DLL”

我想我忘记了一些关于文件描述符<->流关系如何工作的重要信息。 code is available on GitHub。该项目使用 Hunter 以 CMake 友好的方式自动设置 zlib。 来源是跨平台的,mre 提供定义来消除 msvc 警告。

您没有按照 zlib 文档所说的去做,您自己在问题中强调了这一点。使用 dup().