realloc(p,0)真的涉及glibc中的free(p)吗?

时间:2015-02-22 16:01:21

标签: c language-lawyer glibc

我发现某些人和书籍等参考文献表明如果p != NULLp来自之前的分配(例如malloc),则realloc(p, 0)等同于{在GNU / Linux上{1}}。为了支持这一论点man realloc完全以这种方式陈述(强调我的前进):

  

realloc()函数改变指向的内存块的大小   通过ptr来调整字节大小。内容将在以下范围内保持不变   该区域的起点可达到新旧尺寸的最小值。如果   新尺寸大于旧尺寸,增加的内存不会   初始化。如果ptr为NULL,则调用等效于   malloc(size),适用于所有大小值; 如果size等于零,并且   ptr不是NULL,那么调用等同于free(ptr)。除非ptr   为NULL,它必须由之前调用malloc()返回,   calloc()或realloc()。如果指向的区域被移动,则免费(ptr)   完了。

正如您在this question中所发现的那样,C标准没有准确定义应该发生什么,实际行为是实现定义的。更具体地说:

C11§7.22.3/ p1 内存管理功能说:

  

如果请求的空间大小为零,则行为为   implementation-defined:返回空指针,或者   行为就像大小是一些非零值,除了   返回的指针不得用于访问对象。

和C11§7.22.3.5 realloc函数包含:

  

3)(...)如果新对象的内存无法分配,则为旧   对象未取消分配值未更改

     

4)free(p)函数返回一个指向新对象的指针(可能是   与指向旧对象的指针具有相同的值,或空指针   如果无法分配新对象

我写了一些基本代码,以便在realloc提供的mcheck内存检查器的帮助下找出实际行为:

glibc

结果是:

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

int main(void)
{
    int a = 5;
    int *p, *q;

    mtrace();

    p = malloc(sizeof(int));
    q = &a;

    printf("%p\n", (void *) p);
    printf("%p\n", (void *) q);

    q = realloc(p, 0);

    printf("%p\n", (void *) p);
    printf("%p\n", (void *) q);

    return 0;
}

您可能会看到$ gcc -g check.c $ export MALLOC_TRACE=report $ ./a.out 0xfd3460 0x7ffffbc955cc 0xfd3460 (nil) [grzegorz@centos workspace]$ mtrace a.out report Memory not freed: ----------------- Address Size Caller 0x0000000000fd3460 0x4 at /home/grzegorz/workspace/check.c:12 设置为q。似乎NULL并未真正被调用。事实上,除非我的解释不正确,否则不可能:因为free()已经返回realloc指针,所以无法分配新对象,这意味着:

  

旧对象未取消分配且其值未更改

这是对的吗?

4 个答案:

答案 0 :(得分:9)

编辑:你的glibc似乎是2.18之前的版本,在2.18中,mtrace修复了一个错误(参见here)。在2.20 glibc上,您的测试程序报告:&#34;没有内存泄漏。&#34;

在glibc中调用

free。来自当前glibc 2.21(herehere)的来源:

/*
  REALLOC_ZERO_BYTES_FREES should be set if a call to
  realloc with zero bytes should be the same as a call to free.
  This is required by the C standard. Otherwise, since this malloc
  returns a unique pointer for malloc(0), so does realloc(p, 0).
*/

#ifndef REALLOC_ZERO_BYTES_FREES
#define REALLOC_ZERO_BYTES_FREES 1
#endif

void *
__libc_realloc (void *oldmem, size_t bytes)
{
  mstate ar_ptr;
  INTERNAL_SIZE_T nb;         /* padded request size */

  void *newp;             /* chunk to return */

  void *(*hook) (void *, size_t, const void *) =
    atomic_forced_read (__realloc_hook);
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));

#if REALLOC_ZERO_BYTES_FREES
  if (bytes == 0 && oldmem != NULL)
    {
      __libc_free (oldmem); return 0;
    }
#endif

答案 1 :(得分:7)

虽然我对#34;的回应NULL&#34;案例似乎是正确的(请参阅下面的编辑),glibc开发人员decided以使其与之前的C89标准保持一致并拒绝遵守C99 / C11:

  

这是不可能改变的。这是怎么回事   永远实施。 C应记录现有做法。更改   这将意味着引入内存泄漏。

同样mcheck指示具有误导性,因为其他测试用例表明内存 有效地被realloc释放:

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

int main(void)
{
    int *p, *q;

    p = malloc(20 * sizeof(int));
    malloc_stats();

    putchar('\n');

    q = realloc(p, 0);
    malloc_stats();

    return 0;
}

此处输出为:

$ gcc check.c 
$ ./a.out 
Arena 0:
system bytes     =     135168
in use bytes     =         96
Total (incl. mmap):
system bytes     =     135168
in use bytes     =         96
max mmap regions =          0
max mmap bytes   =          0

Arena 0:
system bytes     =     135168
in use bytes     =          0
Total (incl. mmap):
system bytes     =     135168
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

修改

正如hvd在comment中指出的那样,ISO / IEC工作组进行了一些讨论,具体化为Defect Report #400。拟议的修改可能允许在未来修订C标准时采用glibc的现有做法(或可能作为C11的技术勘误1)。

真正喜欢DR#400的主张是:

  

在第7.31.12小节中增加一个新的段落(第2段):

     

调用大小参数等于零的realloc是一个过时的功能。

答案 2 :(得分:2)

由于realloc()传递大小为0时的行为是实现定义...

  

如果请求的空间大小为零,则行为是实现定义的:返回空指针, 行为就像大小一样一些非零值,但返回的指针不得用于访问对象。

......便携式等同于

void * p = malloc(1);
free(p);

需要

void * p = malloc(1);
p = realloc(p, 0)
free(p); /* because of the part after the "or" as quoted above.

之后内存平衡应该是均匀的。

更新涵盖realloc()的“错误”案例:

void * p = malloc(1);
{
  void * q = realloc(p, 0);
  p = q ?q :p;
}
free(p); /* because of the part after the "or" as quoted above.

答案 3 :(得分:2)

realloc处查看glibc代码:http://code.woboq.org/userspace/glibc/malloc/memusage.c.html#realloc。您将在第434行看到当size为0时调用free