【问题标题】:memory leaks when use strlen()使用 strlen() 时内存泄漏
【发布时间】:2021-07-06 03:41:22
【问题描述】:

我是 C 和内存管理的新手,并尝试使用 valgrind 查找内存泄漏,我在 lexer->src_size 中发现了所有问题 不知道为什么我使用strlen()时会出现内存泄漏

valgrind:

==49058== Memcheck, a memory error detector
==49058== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==49058== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==49058== Command: ./loop -s
==49058== 
==49058== Invalid read of size 1
==49058==    at 0x483FF54: strlen (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==49058==    by 0x1093D1: Lexer_Init (lexer.c:11)
==49058==    by 0x109A44: Loop_Compile (loop.c:6)
==49058==    by 0x109AD4: main (in /home/ali/Desktop/loop/loop)
==49058==  Address 0x4a502be is 0 bytes after a block of size 30 alloc'd
==49058==    at 0x483C7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==49058==    by 0x109366: IO_ReadFile (io.c:17)
==49058==    by 0x109ACC: main (in /home/ali/Desktop/loop/loop)
==49058== 
==49058== Conditional jump or move depends on uninitialised value(s)
==49058==    at 0x483FC57: strcat (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==49058==    by 0x10955A: Lexer_LexID (lexer.c:54)
==49058==    by 0x109691: Lexer_NextToken (lexer.c:82)
==49058==    by 0x109A80: Loop_Compile (loop.c:8)
==49058==    by 0x109AD4: main (in /home/ali/Desktop/loop/loop)
==49058==  Uninitialised value was created by a heap allocation
==49058==    at 0x483C7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==49058==    by 0x1093B6: Lexer_Init (lexer.c:9)
==49058==    by 0x109A44: Loop_Compile (loop.c:6)
==49058==    by 0x109AD4: main (in /home/ali/Desktop/loop/loop)
==49058== 
void: 0
main: 0
): 8
{: 9
==49058== Conditional jump or move depends on uninitialised value(s)
==49058==    at 0x483FC57: strcat (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==49058==    by 0x1095F3: Lexer_LexNumber (lexer.c:69)
==49058==    by 0x1096DD: Lexer_NextToken (lexer.c:86)
==49058==    by 0x109A80: Loop_Compile (loop.c:8)
==49058==    by 0x109AD4: main (in /home/ali/Desktop/loop/loop)
==49058==  Uninitialised value was created by a heap allocation
==49058==    at 0x483C7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==49058==    by 0x1093B6: Lexer_Init (lexer.c:9)
==49058==    by 0x109A44: Loop_Compile (loop.c:6)
==49058==    by 0x109AD4: main (in /home/ali/Desktop/loop/loop)
==49058== 
1: 2
+: 4
2: 2
==49058== Invalid read of size 1
==49058==    at 0x109454: Lexer_Advance (lexer.c:22)
==49058==    by 0x109480: Lexer_AdvanceWithToken (lexer.c:28)
==49058==    by 0x10991A: Lexer_NextToken (lexer.c:115)
==49058==    by 0x109A80: Loop_Compile (loop.c:8)
==49058==    by 0x109AD4: main (in /home/ali/Desktop/loop/loop)
==49058==  Address 0x4a502be is 0 bytes after a block of size 30 alloc'd
==49058==    at 0x483C7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==49058==    by 0x109366: IO_ReadFile (io.c:17)
==49058==    by 0x109ACC: main (in /home/ali/Desktop/loop/loop)
==49058== 
}: 10
==49058== 
==49058== HEAP SUMMARY:
==49058==     in use at exit: 234 bytes in 15 blocks
==49058==   total heap usage: 28 allocs, 13 frees, 5,884 bytes allocated
==49058== 
==49058== LEAK SUMMARY:
==49058==    definitely lost: 168 bytes in 10 blocks
==49058==    indirectly lost: 66 bytes in 5 blocks
==49058==      possibly lost: 0 bytes in 0 blocks
==49058==    still reachable: 0 bytes in 0 blocks
==49058==         suppressed: 0 bytes in 0 blocks
==49058== Rerun with --leak-check=full to see details of leaked memory
==49058== 
==49058== For lists of detected and suppressed errors, rerun with: -s
==49058== ERROR SUMMARY: 12 errors from 4 contexts (suppressed: 0 from 0)

io.c:


char *IO_ReadFile(const char *filename)
{
    char *buffer = 0;
    long length;
    FILE *f = fopen(filename, "rb");

    if (f)
    {
        fseek(f, 0, SEEK_END);
        length = ftell(f);
        fseek(f, 0, SEEK_SET);
        buffer = malloc(length);
        if (buffer)
        {
            fread(buffer, 1, length, f);
        }
        fclose(f);
    }

    return buffer;
}

循环.c

void Loop_Compile(char *src)
{
    lexer_t *lexer = Lexer_Init(src);
    token_t *token = 0;
    while ((token = Lexer_NextToken(lexer))->kind != TOKEN_EOF)
    {
        printf("%s: %d\n", token->value, token->kind);
    }
}

lexer.c

lexer_t *Lexer_Init(char *src)
{
    lexer_t *lexer = malloc(sizeof(lexer_t));
    lexer->src = src;
    lexer->src_size = strlen(src); // <----- Here
    lexer->index = 0;
    lexer->cc = src[lexer->index];
    return lexer;
};

和词法分析器结构类型:

typedef struct LEXER_STRUCT
{
    char *src;
    size_t src_size;
    char cc; // cc: current char
    unsigned int index;
} lexer_t;

main.c

int main(int argc, char* argv[]) {
    if (argc < 2) {
        printf("Please Add A File\n");
        return 1;
    }
    Loop_Compile(IO_ReadFile("./examples/main.loop"));
    return 0;
}

【问题讨论】:

  • 我在您发布的代码中看不到任何可以解释 valgrind 错误消息的内容。请提供minimal reproducible example
  • "I know what the problem is." -- 我没有那种印象。我的猜测是问题出在您未发布的某些代码中。我无法验证这一点,因为您没有提供minimal reproducible example
  • @AliC:您是否阅读过有关如何创建minimal reproducible example 的说明?如果在没有完整程序代码的情况下无法重现错误,则表明问题出在代码的其他位置。
  • 如果 valgrind 在strlen 中报告了invalid read,那么这表明函数参数src 没有指向一个有效的字符串,或者该字符串的内存没有正确分配(例如缓冲区溢出)。这与内存“泄漏”无关。您可能想检查 srcdebugger 中指向的内容。
  • @AliC:我希望你现在明白提供minimal reproducible example 的重要性。在那之前,你的问题是无法回答的。让您特别难以帮助的是,在您的问题的revision 3 中,您提供了关于如何调用Lexer_Init 的错误代码。此错误代码无法解释 valgrind 错误,只有您的实际代码可以。

标签: c memory memory-management memory-leaks valgrind


【解决方案1】:

strlen(src); 失败,因为 src 不一定指向 字符串

IO_ReadFile() 不返回指向 string 的指针,因为函数未能分配或附加 null 字符

试试

    //buffer = malloc(length);
    buffer = malloc(length + 1);
    if (buffer)
    {
        fread(buffer, 1, length, f);
        buffer[length] = 0; // add
    }

可能存在其他问题。

【讨论】:

    【解决方案2】:

    不知道为什么我使用strlen()时会出现内存泄漏

    Valgrind 没有报告内存泄漏。它正在报告

    ==49058== Invalid read of size 1
    

    请求读取的位置是

    ==49058==  Address 0x4a502be is 0 bytes after a block of size 30 alloc'd
    ==49058==    at 0x483C7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==49058==    by 0x109366: IO_ReadFile (io.c:17)
    

    这是分配空间的溢出,由您尝试计算其长度的数据未以空值终止引起。如果您希望IO_ReadFile 提供的数据适合与字符串函数一起使用,那么它必须分配足够的空间以在文件内容之后包含一个终止符。

    当程序没有在 Valgrind 下运行时,它的行为似乎符合您的预期,因为这种溢出时的行为是未定义的。它可能在实践中起作用,因为下一个位置恰好在程序的可访问内存空间内(很可能)并且它恰好包含一个零字节(这并不令人难以置信,尤其是在程序运行的早期)。然而,这是一个严重的错误。

    【讨论】:

      猜你喜欢
      • 2011-10-12
      • 2013-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-17
      相关资源
      最近更新 更多