realloc 会改变它的第一个参数吗?
如果您的意思是更改作为参数传递的变量的值,答案是否定的。这一点与特定的realloc() 函数无关,而更普遍地与语言处理参数的方式有关。 C 语言在将参数传递给函数之前通常在堆栈上生成每个参数的私有副本,因此对它们的每次更改都被限制在本地,并且在函数返回和堆栈重用时丢失。形式上,C 语言通过值传递几乎所有类型(数组是一个众所周知的例外)。无论如何,我会回到下面的论点。
是否改变第一个参数取决于实现?
当然不是。如上所述,这取决于语言。
它不应该是 const 有什么原因吗?作为一个反例 memcpy
使其 src 参数为 const。
当然是有原因的。
忘记与void* realloc (void* ptr, size_t size)没有联系的void * memcpy ( void * destination, const void * source, size_t num ),让我们考虑动态内存的管理取决于特定的本地实现,但基本上所有的分配例程都是基于内存池,通常分成小块,从哪里派生返回给我们程序的内存块。我们可以想象,当我们需要缩小块时,系统将删除一些块,返回一个较小的块,这些块偶然保留在同一地址,但如果我们需要扩展,可能我们块后面的块已经分配,我们无法继续.
在嵌入式 8 位 micro 上可能会发生实际内存块无法扩展的情况,但另一个内存区域对于范围足够大,在这种情况下,我们可以将以前的块数据复制到新的块并返回。但在这种情况下,我们在内存中有不同的地址。
但是malloc() 必须是通用的,独立于实现它的机器,从 8 位嵌入式应用程序到具有千兆字节可用内存和虚拟内存支持的 64 位桌面。出于这个原因,标准必须提供一个适合所有情况的定义。
第二点是如何传递重新分配的结果,通过/失败,如果使用了对内存块指针的引用(即传递&ptr),如果返回 NULL 失败,原始指针将丢失!。用户为了保存它,必须先复制指向realloc() 的指针,但这个过程很不自然,容易出错。
出于这个原因,在标准库中,这个问题从不同的角度来解决:重新分配将正式返回一个新分配的内存块,其中已经复制了以前的内存块数据。程序员只需要在使用前检查结果(见下面的代码示例)。
函数定义中的标准非常明确,正如其他答案中已经提到的那样,为了完整起见,我在下面报告。来自 ISO/IEC 9899:2017 §7.22.3.5 realloc 函数:
realloc 函数释放 ptr 指向的旧对象并
返回一个指向新对象的指针,该对象的大小由 size 指定。
新对象的内容应与旧对象的内容相同
释放前的对象,直到新旧对象中的较小者
尺寸。
新对象中超出旧对象大小的任何字节都有
不确定的值。
如果 ptr 是空指针,realloc 函数的行为类似于 malloc
指定大小的函数。否则,如果 ptr 不匹配
内存管理函数早先返回的指针,或者如果
已通过调用 free 或 realloc 函数释放空间,
行为未定义。
如果 size 不为零且未分配新对象的内存,则
旧对象没有被释放。
如果 size 为零并且没有为新对象分配内存,则它是
实现定义是否释放旧对象。如果
旧对象未释放,其值应保持不变。
realloc 函数返回一个指向新对象的指针(可能
具有与指向旧对象的指针相同的值)或空指针
如果新对象尚未分配。
因为你不知道realloc()是返回一个新对象还是以前的对象,甚至NULL在出错的情况下,你应该考虑realloc()作为总是返回一个新对象,因此代码:
int* list = NULL;
void* mem;
mem = realloc(list, 64);
printf("Address of `list`: %p\n", list);
至少有两个原因是错误的:
- 显然因为如果
realloc() 返回一个新对象并释放
旧内存,变量list 包含无效指针。此外,它可能无法返回 NULL,在这种情况下,前一个块仍然有效。
- 因为您不能期望以任何方式更改
list,将其作为函数中的本地参数传递。当然list 将保留其以前的值NULL。
虽然将空指针传递给realloc() 是符合标准的,因为它明确表示在这种情况下行为将与malloc() 相同,传递零大小 行为是implementation-defined 暗示前一个块将被一些编译器释放,但不会被其他一些编译器释放。后者意味着行为可以在编译器的基础上改变,在你的机器上我们可以推断出编译器的行为显然是释放块,因为你得到了双重释放错误和realloc()返回的空指针。另请注意,在后一种情况下当将零大小传递给realloc()时,返回的NULL 并不意味着发生了故障,并且该函数是成功的,但如果失败你将无法正确理解是否有失败。这是功能的模糊性(或者据我所知,欢迎使用 cmets)。
使用realloc() 时要遵循的黄金法则基本上如下:
- 请记住,从函数 返回的对象始终是
新对象,您必须保存它。
- 因为
realloc() 可能会失败并返回NULL 指针,所以永远不要使用
代码如下,因为如果失败,我们将覆盖旧的
对象指针失去恢复数据或释放的可能性
以前的对象。始终使用临时变量来检查返回
价值。
示例代码:
void *p = malloc(SIZE);
/* Wrong approach we overwrite anyway teh pointer */
p = realloc(p, 2*SIZE);
/** Correct approach */
void *pTmp = realloc(p, 2*SIZE);
if (NULL == pTmp)
{
//Error manage code
}
else
{
p = pTmp; //assign value
}
现在您可能会问,为什么在许多机器上,如台式机、智能手机等具有虚拟内存管理功能,经常碰巧从realloc() 返回的内存地址不变。关键是,由于虚拟内存管理,可以将更多的物理不连续内存块添加到虚拟内存链中,然后可以操纵虚拟内存描述符,将相应的虚拟地址映射到每个物理块,用户可以看到平坦的连续虚拟内存空间。