【问题标题】:Program with realloc behave differently in Valgrind具有 realloc 的程序在 Valgrind 中的行为不同
【发布时间】:2012-11-27 08:06:12
【问题描述】:

我写了一个函数来读取一个带有 fgets 的字符串,它使用 realloc() 使缓冲区在需要时增长:

char * read_string(char * message){
    printf("%s", message);
    size_t buffsize = MIN_BUFFER;
    char *buffer = malloc(buffsize);
    if (buffer == NULL) return NULL;
    char *p;
    for(p = buffer ; (*p = getchar()) != '\n' && *p != EOF ; ++p)
        if (p - buffer == buffsize - 1) {
            buffer = realloc(buffer, buffsize *= 2) ;
            if (buffer == NULL) return NULL;
        }
    *p = 0;
    p = malloc(p - buffer + 1);
    if (p == NULL) return NULL;
    strcpy(p, buffer);
    free(buffer);
    return p;
}

我编译了程序并尝试了它,它按预期工作。但是当我用 valgrind 运行它时,当读取的字符串 >= MIN_BUFFER 并且 valgrind 说:

(...)
==18076==  Invalid write of size 1
==18076==    at 0x8048895: read_string (programme.c:73)
==18076==    by 0x804898E: main (programme.c:96)
==18076==  Address 0x41fc02f is 0 bytes after a block of size 7 free'd
==18076==    at 0x402BC70: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==18076==    by 0x8048860: read_string (programme.c:76)
(...)
==18076== Warning: silly arg (-48) to malloc()
(...)

我在 *p=0; 之间添加了一个 printf 语句和 p=malloc... 它确认传递的 arg 的值为 -48。 我不知道单独启动和使用 valgrind 时程序的运行方式不同。我的代码有问题还是只是 valgrind 错误?

【问题讨论】:

  • 旁注:如果realloc() 返回NULL,则您的代码正在泄漏内存 - 在这种情况下,在返回NULL 之前使用临时变量并释放前一个指针。
  • *p = 0; 可以写一个超出分配的大小。

标签: c valgrind realloc


【解决方案1】:

正如 man 3 realloc 所说

...该函数可能会将内存块移动到新位置。

这是什么意思,是那个

p = malloc(p - buffer + 1);

是问题所在。如果调用了 realloc(),缓冲区可能指向一个新的内存块和表达式

(p - buffer)

没有任何意义。

【讨论】:

  • “EDITED”段落不正确:p - buffer + 1 不是指针,它是ptrdiff_t 类型的数字。 ptrdiff_t 是有符号整数类型,因此可以转换为兼容的整数类型,例如 size_t。此外,您在修改后的代码中为 p 选择的 char 类型将缓冲区限制为 127 个字节,之后它会溢出并通过写入未分配的内存来破坏堆。
  • 感谢您更新答案。在 C 语言中,p - buffer未定义的行为,这意味着您不仅会得到无意义的结果,而且程序可能会在某些架构或编译器上崩溃。这种行为是未定义的,因为只允许减去指向同一数组中对象的指针;引用§6.5.6.3.9:“当减去两个指针时,两者都应指向同一个数组对象的元素,或者指向数组对象最后一个元素的元素。” (该标准还在第 4.2 节中明确定义违反的“应”约束构成未定义的行为。)
【解决方案2】:

realloc 返回一个指向所请求大小的新缓冲区的指针,其内容与传入的指针相同,假设传入的指针先前由mallocrealloc 返回。它不保证它是同一个指针。 Valgrind 很可能会修改 realloc 的行为,但将其保持在规范范围内。

由于您是在循环中调整内存大小,因此最好将您在buffer 中的位置跟踪为与buffer 开头的偏移量而不是指针。

【讨论】:

    【解决方案3】:

    当您重新分配缓冲区时,您的指针“p”仍指向旧缓冲区。

    这将占用内存,并导致未来的分配使用虚假值。

    【讨论】:

    • 感谢大家:@JasonD、@davak、@H2CO3、@MSN。你的回答很有帮助。我将代码更改为:if (p - buffer == taillbuff - 1) { char *r = realloc(buffer, taillbuff *= 2) ; if (r == NULL) { free(buffer); return NULL; } else buffer = r; p = buffer + taillbuff/2 - 1; }
    猜你喜欢
    • 2017-06-29
    • 2014-01-31
    • 1970-01-01
    • 2012-03-22
    • 2014-08-18
    • 1970-01-01
    • 2016-08-17
    • 2013-06-08
    • 1970-01-01
    相关资源
    最近更新 更多