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

Grz*_*ski 21 c glibc language-lawyer

我发现有些人和像图书这样的参考文献表明,如果p != NULLp来自之前的分配(例如by malloc),则realloc(p, 0)相当于free(p)GNU/Linux.为了支持这一论点,man realloc完全以这种方式陈述(强调我的前进):

realloc()函数将ptr指向的内存块的大小更改为size字节.内容将在从区域开始到新旧尺寸的最小范围内保持不变.如果新大小大于旧大小,则不会初始化添加的内存.如果ptr为NULL,则对于所有size值,调用等效于malloc(size); 如果size等于零,并且ptr不为NULL,则该调用等效于free(ptr).除非ptr为NULL,否则必须由之前调用malloc(),calloc()或realloc()返回.如果指向的区域被移动,则完成自由(ptr).

正如您在此问题中所发现的那样,C标准没有准确定义应该发生什么,实际行为是实现定义的.进一步来说:

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

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

和C11§7.22.3.5realloc 函数包含:

3)(...)如果无法分配新对象的内存,则不会释放旧对象,并且其值不变.

4)该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;
}
Run Code Online (Sandbox Code Playgroud)

结果是:

$ 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
Run Code Online (Sandbox Code Playgroud)

正如你可能会看到q被设置为NULL.似乎free()并没有真正被称为.实际上除非我的解释是不正确的,否则它是不可能的:因为realloc已经返回NULL指针,所以无法分配新对象,这意味着:

旧对象未释放,其值不变

它是否正确?

456*_*976 9

编辑:您的glibc似乎是2.18之前,在2.18中,mtrace中修复了一个错误(参见此处).在2.20 glibc上,您的测试程序报告:"没有内存泄漏."

free在glibc中调用.从当前的glibc 2.21(这里这里)的来源:

/*
  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
Run Code Online (Sandbox Code Playgroud)


Grz*_*ski 7

虽然我对"返回NULL"案例的解释似乎是正确的(请参阅下面的编辑),但glibc开发人员决定使其与之前的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;
}
Run Code Online (Sandbox Code Playgroud)

这里的输出是:

$ 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
Run Code Online (Sandbox Code Playgroud)

编辑:

正如hvd在评论中指出的那样,ISO/IEC工作组进行了一些讨论,实现为缺陷报告#400.拟议的变更可能允许glibc在将来修订C标准时进行现有做法(或可能作为C11的技术勘误1).

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

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

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

  • +1引用bug跟踪器.我已经重新打开了这个问题,因为这个决定是由glibc的前独裁者做出的,他现在已经开始了. (2认同)
  • @R ..是否有任何glibc错误?如果我正确阅读[DR#400](http://open-std.org/JTC1/SC22/WG14/www/docs/dr_400.htm),C改为重新允许C90和glibc行为.或者那是不是被接受了? (2认同)