我发现某些人和书籍等参考文献表明如果p != NULL
和p
来自之前的分配(例如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
指针,所以无法分配新对象,这意味着:
旧对象未取消分配且其值未更改
这是对的吗?
答案 0 :(得分:9)
编辑:你的glibc似乎是2.18之前的版本,在2.18中,mtrace修复了一个错误(参见here)。在2.20 glibc上,您的测试程序报告:&#34;没有内存泄漏。&#34;
在glibc中调用 free
。来自当前glibc 2.21(here和here)的来源:
/*
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
。