简体   繁体   English

没有 memory 泄漏,但使用 malloc 和文件指针时来自 valgrind 的错误令人困惑

[英]No memory leak, but confusing errors from valgrind when using malloc and file pointers

This reads from an already existing text file and creates 6 outfiles of little snippets from the infile.这从一个已经存在的文本文件中读取,并从 infile 中创建 6 个小片段的 outfiles。 It works as predicted, but upon running valgrind I get multiple errors.它按预期工作,但在运行 valgrind 时出现多个错误。 However, there is no memory leak:但是,没有 memory 泄漏:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char buffer[5];
    FILE* infile = fopen("clams.txt", "r");
    if (infile == NULL)
    {
        return 1;
    }

    for (int i = 0; i < 6; i++)
    {
        fread(buffer, sizeof(char), 5, infile);

        char* filename = malloc(sizeof(char) * 4);                        //line 17
        sprintf(filename,"%03i.txt", i);
        printf("%c%c%c\n", filename[0], filename[1], filename[2]);        //19

        FILE* outfile = fopen(filename, "w");                             //21
        if (outfile == NULL)
        {
            return 2;
        }
        fwrite(buffer, sizeof(char), 5, outfile);

        fclose(outfile);
        free(filename);
    }
    fclose(infile);
}

Valgrind said:瓦尔格林德 说:

==1042== Invalid write of size 1
==1042==    at 0x4C762B4: _IO_default_xsputn (genops.c:394)
==1042==    by 0x4C762B4: _IO_default_xsputn (genops.c:370)
==1042==    by 0x4C5B165: __vfprintf_internal (vfprintf-internal.c:1719)
==1042==    by 0x4C69278: __vsprintf_internal (iovsprintf.c:95)
==1042==    by 0x4C46047: sprintf (sprintf.c:30)
==1042==    by 0x401234: main (filename.c:18)
==1042==  Address 0x4dd62a4 is 0 bytes after a block of size 4 alloc'd
==1042==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1042==    by 0x401218: main (filename.c:17)
==1042== 
==1042== Invalid write of size 1
==1042==    at 0x4C6927E: __vsprintf_internal (iovsprintf.c:97)
==1042==    by 0x4C46047: sprintf (sprintf.c:30)
==1042==    by 0x401234: main (filename.c:18)
==1042==  Address 0x4dd62a7 is 3 bytes after a block of size 4 alloc'd
==1042==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1042==    by 0x401218: main (filename.c:17)
==1042== 
000
==1042== Syscall param openat(filename) points to unaddressable byte(s)
==1042==    at 0x4CF1EAB: open (open64.c:48)
==1042==    by 0x4C74195: _IO_file_open (fileops.c:189)
==1042==    by 0x4C74459: _IO_file_fopen@@GLIBC_2.2.5 (fileops.c:281)
==1042==    by 0x4C66B0D: __fopen_internal (iofopen.c:75)
==1042==    by 0x4C66B0D: fopen@@GLIBC_2.2.5 (iofopen.c:86)
==1042==    by 0x401275: main (filename.c:21)
==1042==  Address 0x4dd62a4 is 0 bytes after a block of size 4 alloc'd
==1042==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1042==    by 0x401218: main (filename.c:17)
==1042== 
001
002
003
004
005
==1042== 
==1042== HEAP SUMMARY:
==1042==     in use at exit: 0 bytes in 0 blocks
==1042==   total heap usage: 20 allocs, 20 frees, 32,000 bytes allocated
==1042== 
==1042== All heap blocks were freed -- no leaks are possible
==1042== 
==1042== For lists of detected and suppressed errors, rerun with: -s
==1042== ERROR SUMMARY: 30 errors from 3 contexts (suppressed: 0 from 0)

( filename.c is the name of my program) filename.c是我的程序的名称)

The only way I've been able to eliminate the errors is by changing line 17 to allocate 8 or more bytes.我能够消除错误的唯一方法是更改第 17 行以分配 8 个或更多字节。 Anything between 4 and 7 inclusive gives me errors.介于 4 和 7 之间的任何东西都会给我带来错误。 I am confused as to why this is, given each string of 3 chars and the \0 should only take 4 bytes to store.我很困惑为什么会这样,因为每个字符串由 3 个字符组成,而\0应该只需要 4 个字节来存储。

If I kept malloc(4) , should I be concerned about those errors?如果我保留malloc(4) ,我应该担心这些错误吗? For a similar program that uses much more memory (eg images instead of text), can those errors eventually lead to a crash or segmentation fault?对于使用更多 memory(例如图像而不是文本)的类似程序,这些错误最终会导致崩溃或分段错误吗?

The problem is clear by looking at the following two lines:通过查看以下两行,问题就很清楚了:

char* filename = malloc(sizeof(char) * 4); // 17
sprintf(filename, "%03i.txt", i);          // 18

At line 17 you allocate 4 bytes, which is enough space for a string of 3 characters plus a terminator ( \0 ).在第 17 行,您分配了 4 个字节,这对于 3 个字符加上一个终止符 ( \0 ) 的字符串来说已经足够了。 Then, right after that, you write into that string more than 3 characters: at least 4 just from .txt , and then some more due to the %03i .然后,在那之后,您在该字符串中写入超过 3 个字符:至少 4 个来自.txt ,然后由于%03i更多。

You should instead allocate more space.相反,您应该分配更多空间。 A conservative estimate for the needed size of the string holding the file name would be 4 ( .txt ) + 3 (since i can assume values from 0 to 5 and is padded to 3 decimal digits) + 1 ( \0 terminator), so a total of 8 bytes.保存文件名的字符串所需大小的保守估计是 4 ( .txt ) + 3 (因为i可以假设值从05并填充到 3 个十进制数字) + 1 ( \0终止符),所以一共8个字节。

This is why changing that malloc(4) into a malloc(8) does not produce errors.这就是为什么将malloc(4)更改为malloc(8)不会产生错误的原因。

By the way, for such a small buffer, you can avoid complicating things with malloc / free and just declare it on the stack as a local variable:顺便说一句,对于这么小的缓冲区,您可以避免使用malloc / free使事情复杂化,只需在堆栈上将其声明为局部变量:

char filename[8];

If I kept malloc(4) , should I be concerned about those errors?如果我保留malloc(4) ,我应该担心这些错误吗?

Yes, you're writing past the end of an allocated buffer, which is undefined behavior in C, which means your program can easily crash, segfault, or do worse things.是的,您正在写入已分配缓冲区的末尾,这是 C 中未定义的行为,这意味着您的程序很容易崩溃、段错误或做更糟糕的事情。

For a similar program that uses much more memory (eg images instead of text), can those errors eventually lead to a crash or segmentation fault?对于使用更多 memory(例如图像而不是文本)的类似程序,这些错误最终会导致崩溃或分段错误吗?

Even for the small program you just provided those errors can result in a crash, so yes.即使对于您刚刚提供的小程序,这些错误也可能导致崩溃,所以是的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM