在C语言中,是否可以在不移动内存的情况下缩小已分配的内存?

4

有没有一种在C语言中释放内存的方法/函数,而不可能将其移动到新指针?

谢谢!


1
你到底想要实现什么?如果你不再需要分配的一些内存(因此某些变量),只需释放这些变量即可。 - JSQuareD
@JSQuareD:我猜OP想要避免realloc()可能带来的复制开销。 - Oliver Charlesworth
有些实现不会移动数据,除非一个较小的块可以合理地容纳它(它必须能够稍微增长一点,否则没有移动的价值,因为当再次变大时,它只会被移回去)。如果它不移动您的数据,它将简单地将额外释放的字节添加到空闲列表中,并在相同地址保持您的数据完整。 - user539810
2个回答

4
根据C99标准的严格解释,调用realloc()时,包括将分配块的大小减小在内,都可能返回与参数不同的指针。事实上,根据标准的严格解释,您不允许将旧指针与新指针进行比较,因为将旧指针传递给realloc()会使所有现有副本变为不确定状态。
引用块中写道:

realloc函数释放由ptr指向的旧对象,并返回一个指向具有指定大小的新对象的指针。(C99 7.20.3.4:2)

然而,在我的系统中,realloc() man页上写道:

如果没有足够的空间来扩大ptr指向的内存分配,则realloc()创建一个新的分配,将由ptr指向的旧数据的尽可能多的部分复制到新的分配中,释放旧分配,并返回分配的内存的指针。

这或多或少意味着,仅在用于扩大块时,realloc()只能移动数据并返回新指针。这表明在此平台上,可以使用realloc()来缩小从malloc()获得的块的大小而无需移动它。
如果我要使用realloc()来缩小已分配块的大小而不移动它,我将使用以下检查:
uintptr_t save = old;
void * new = realloc(old, …);
assert (save == (uintptr_t) new);

这些预防措施并非没有道理,例如观察一下在这个非正式未定义行为比赛中的第二名。

2021年编辑:多年之后,更进一步的重新解释和演变C语言定义为高级语言的努力意味着即使以上三行都是合法的,即使执行它们后,您也不能使用旧版old来访问由new指向的块,尽管这些指针指向同一位置。请参见有关C语言指针来源的当前论文


我唯一可能会补充的是,比较指针是多么无意义。如果realloc返回NULL,则您的旧指针未更改。否则,应使用新指针。赋值比分支和可能的赋值更快,因此请在确保新指针不为NULL后将新指针分配给旧指针。如果它为NULL,则数据仍在旧指针中,并且可以安全地访问,尽管它没有调整大小。在这种情况下所做的操作取决于重新分配的上下文和意图。 - user539810
@ChronoKitsune 如果 (新 != 旧) { 代价高昂的更新 numerous copies of old pointer } 是一种在现有代码中有时可以看到的习语。我不对其价值发表立场,我只是说它存在并且在技术上未定义操作。 - Pascal Cuoq
如果在其他地方有另一个指向相同地址的变量,那么在重新分配内存后,你需要将所有这些指针都盲目地分配给新指针,因为它们无论如何都是无效的。进行赋值操作不应该成本高昂。如果是这样的话,我会支持尽可能避免这种更新的一级间接层次。这样,将新指针分配给旧指针将更新其他副本,因为它们将指向旧指针的地址,而不是旧指针曾经指向的相同地址。 - user539810

-1
根据我的理解,不需要。因为你需要将数据复制到新的位置,然后使用free()函数释放之前的位置。
这是我从你的问题中理解的。
请提出明确的问题。我所理解的是,@JsQuareD也从你的问题中得出了相同的理解。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接