C getline内存泄漏不同的行为

标签 c memory-leaks valgrind getline

我有一个关于函数的问题 getline() ,正如 valgrind 所报告的那样,这在关于内存使用的两种情况下似乎表现不同。 .我发布了两个案例的代码并解释了行为。
我希望有人能指出我正确的方向。
第一种情况getline()在 while 循环中调用,读取缓冲区中文本文件的所有行。然后缓冲区在循环结束时仅被释放一次:在这种情况下 valgrind没有错误(没有发生泄漏)。

int main(int argc, char* argv[])
{
    char* buffer = NULL;
    size_t bufsize = 0;
    ssize_t nbytes;
    int counter = 0;
    char error = 0;

    FILE* input_fd = fopen(argv[1], "r");

    while ((nbytes = getline(&buffer, &bufsize, input_fd)) != -1)
    {
        counter += 1;
    }

    free(buffer);
    fclose(input_fd);

    return 0;
}
第二种情况
同一个循环调用一个函数,该函数依次调用 getline() ,传递相同的缓冲区。同样,缓冲区仅在循环结束时被释放一次,但在这种情况下 valgrind报告内存泄漏。事实上,让程序运行并查看 RSS,我可以看到它随着循环的进行而增加。请注意,在循环内添加一个 free(每个循环释放缓冲区)问题就会消失。这是代码。
int my_getline(FILE* lf_fd, char** lf_buffer)
{
    ssize_t lf_nbytes = 0;
    size_t lf_bufsiz = 0;
    lf_nbytes = getline(lf_buffer, &lf_bufsiz, lf_fd);
    if (lf_nbytes == -1)
        return 1;
    return 0;
}

int main(int argc, char* argv[])
{
    char* lf_buffer = NULL;
    size_t bufsize = 0;
    ssize_t nbytes;
    int counter = 0;
    int new_line_counter = 0;
    char error = 0;

    FILE* lf_fd = fopen(argv[1], "r");

    while ((my_getline(lf_fd, &lf_buffer)) == 0)
    {
        // Added to allow measuring the RSS
        sleep(2);
   
        // If I uncomment this, no memory leak occurs
        //free(lf_buffer);
    }

    free(lf_buffer);
    fclose(lf_fd);

    return 0;
}
Valgrind 输出
==9604== Memcheck, a memory error detector
==9604== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9604== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==9604== Command: ./my_getline_x86 /media/sf_Scambio/processes.log
==9604== HEAP SUMMARY:
==9604==     in use at exit: 1,194 bytes in 2 blocks
==9604==   total heap usage: 8 allocs, 6 frees, 11,242 bytes allocated
==9604== 
==9604== 1,194 bytes in 2 blocks are definitely lost in loss record 1 of 1
==9604==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-
linux.so)
==9604==    by 0x48E371D: getdelim (iogetdelim.c:102)
==9604==    by 0x1092B3: my_getline (my_getline.c:14)
==9604==    by 0x10956A: main (my_getline.c:38)
==9604== 
==9604== LEAK SUMMARY:
==9604==    definitely lost: 1,194 bytes in 2 blocks
==9604==    indirectly lost: 0 bytes in 0 blocks
==9604==      possibly lost: 0 bytes in 0 blocks
==9604==    still reachable: 0 bytes in 0 blocks
==9604==         suppressed: 0 bytes in 0 blocks
==9604== 
==9604== For lists of detected and suppressed errors, rerun with: -s
==9604== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

最佳答案

第一个程序没问题。
第二个的问题来自 getline() 的缓冲区长度参数。 .您的 my_getline()始终将其设置为 0,即 getline()每次分配一个新缓冲区(至少,使用您正在使用的 glibc 实现;见下文)。将其更改为

int my_getline(FILE* lf_fd, char** lf_buffer, size_t* lf_bufsiz)
{
    ssize_t lf_nbytes = 0;
    lf_nbytes = getline(lf_buffer, lf_bufsiz, lf_fd);
    if (lf_nbytes == -1)
        return 1;
    return 0;
}
并传递一个指向 size_t 的指针变量在使用时最初初始化为 0。现有bufsize main() 中的变量看起来适合使用:
//...
while ((my_getline(lf_fd, &lf_buffer, &bufsize)) == 0)
// ...

虽然很容易解决,但您遇到的内存泄漏似乎是 getline() 的 glibc 实现中的错误。 .
来自 POSIX documentation :

If *lineptr is a null pointer or if the object pointed to by *lineptr is of insufficient size, an object shall be allocated as if by malloc() or the object shall be reallocated as if by realloc(), respectively, such that the object is large enough to hold the characters to be written to it...


glibc manpage :

Alternatively, before calling getline(), *lineptr can contain a pointer to a malloc(3)-allocated buffer *n bytes in size. If the buffer is not large enough to hold the line, getline() resizes it with realloc(3), updating *lineptr and *n as necessary.


这些表明,在您遇到的情况下,您正在传递有效的非 NULL指向内存的指针并说它的长度为 0,该函数应该使用 realloc()调整它的大小。然而,glibc implementation支票 *lineptr == NULL || *n == 0如果为真,则覆盖 *lineptr使用新分配的缓冲区,导致您看到的泄漏。比较 NetBSD implementation , 使用 realloc()对于所有分配( realloc(NULL, x) 等效于 malloc(x) ),因此不会导致原始代码泄漏。这并不理想,因为它会导致 realloc()在每次使用时,而不是仅在缓冲区不足以容纳当前行时(与上面的固定版本不同),但它有效。

关于C getline内存泄漏不同的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64390291/

相关文章:

C 编程语言 , 制作有用的程序

docker - Nifi 1.6.0内存泄漏

ios - iOS 中的核心数据内存占用不断增长

c++ - valgrind 提示条件跳转或移动取决于未初始化的值

c++ - 使用 boost 在 memcpy 中重叠源和目标 block

python - 从python程序执行c程序

c - 这就是我在链表中​​创建链表的方式吗?

c - 我什么时候释放变量decodeSet1?

c - 超过 1 个链表 - 添加元素

c - 如何使用leaks命令行工具查找内存泄漏?