使用 realloc (X, 0) 代替 free() 并使用字符串长度为 +1 的 malloc

Posted

技术标签:

【中文标题】使用 realloc (X, 0) 代替 free() 并使用字符串长度为 +1 的 malloc【英文标题】:Using realloc (X, 0) instead of free() and using malloc with length of a string +1 【发布时间】:2013-05-21 12:33:58 【问题描述】:

所以这次我真的不知道怎么写标题了。首先,我想说,如果问题与“作业”有关,我已经在此页面上看到了几个关于警告的 cmets。 我的是,但它也完成了,我只是想进一步了解代码发生了什么。

我也阅读了一段时间的帖子和书籍,但我认为我仍然缺少一些东西。

在我使用的代码中有两行我不太理解的代码。这项工作是关于获取用作参数的任何文件(如果它是 0 个文件,则从标准输入读取),并将其向后打印到标准输出上。所有这一切,在我试图放入标签时谈论 C。

第一个问题是这样的:

array = realloc (array, 0);

数组定义为

char **array;

问题是 free 不起作用,它没有释放使用的空间(也许我用错了?在其他地方我知道如何使用它,但这次不知道)。根据我所做的测试和阅读的内容,我相信 realloc 也在做同样的事情,但我不是 100%。

第二个是:

char* alloc = malloc (strlen ((char*)string)+1);

其中 alloc 用于复制我要放入数组的行的确切长度,因此我可以在此之后将文本向后打印。

问题是为什么我必须使用 +1。我的意思是如果我因为某种原因不使用它就不起作用,我尝试了不同的数字并且它每次都有效,但如果我不这样做 +1 它就不能正常工作。

我知道这个问题可能太模糊而且写得不好,无法真正回答,但我又不确定,我尽力解释自己(英语没有母语,因为这可能很明显)。

【问题讨论】:

realloc(array, 0)等同于free(array)。这个成语是错误的。我现在没有时间写答案,但希望有人能解释清楚。 您如何得出free 不起作用的结论? 请解释“不起作用”是什么意思。程序会崩溃吗?打印什么错误信息? 请注意free 的语义是使内存可用于后续分配 并且 减少@ 报告的一些可用空间计数器987654328@、memfree 或类似的系统实用程序。 如果您的程序在free 上崩溃,则您的代码中存在错误。 realloc(X, 0) 可能等同于也可能不等同于 free(X)。它依赖于实现。在您的系统上,它们显然不等效,因为free 崩溃而realloc 没有。通过禁用崩溃,您只需假装错误不存在。然而它仍然存在。我会建议尝试找到它。 【参考方案1】:

realloc 在大小为 0 时的行为在 C11(当前版本)中有所不同。标准说(C11 为 7.20.3.1,C1x 为 7.22.3.1)

如果请求的空间大小为零,则行为是 实现定义:要么返回一个空指针,要么 行为就好像大小是某个非零值,除了 返回的指针不得用于访问对象

所以,使用free,不要依赖realloc

通过char* 处理字符串时,请务必记住为空终止符\0 添加一个额外字符。这是显示字符串结束位置的常用方法(另一种是显式字符串长度)。

当使用mallocfree 时,记住它们必须完全匹配。您需要freemallocrealloc 返回的确切指针(值)。

【讨论】:

【参考方案2】:

array = realloc(array, 0);

在某些 C 实现中,大小为零的 Realloc 是 equivalent to free(),但不是全部。

问题是 free 不起作用,它不会释放使用的空间

想想char **array 的含义以及它在您的应用程序中的分配方式。指针到指针通常用作二维数组,表示为数组的数组。大多数应用程序分配这些with multiple calls to malloc()。 array 只是 char * 的数组,其中该数组的每个元素都是 char 的数组。只需在 char * 的数组上调用 free() 将释放 char * 的数组,但不会释放 char 的每个数组。

你需要多次调用free()as described here。

我尝试了不同的数字,它每次都能正常工作,但如果我不这样做 +1,它就不能正常工作。

C 字符串是 null 终止的,这意味着程序通过在字符串末尾放置一个 nul 字符来跟踪字符串的结束位置。这意味着长度为 N 的 C 字符串需要 N 个字符的空间,加上一个 nul 字符。那么内存空间的总长度就是N+1。

【讨论】:

如果 free(p) 由于您所描述的情况而无法正常工作,那么 realloc(p, 0) 也不会起作用。 realloc(p,0) 不等同于 C11 中的 free(p) realloc(p,0) 仅在 Linux 上等效于 free(p),在标准 C 中不等效,POSIX 也不等效。 我修正了帖子中关于 reallocfree 的声明。 如果它在 Linux 上是等价的,那就是一个错误。 realloc(p,0) 形式上等价于free(p); malloc(0);,在 Linux(嗯,glibc)上,malloc(0) 为每次调用返回一个唯一的非空指针,而不是空指针。【参考方案3】:

第一个问题:

realloc(array, 0)等同于free(array)

标准(C99,§7.20.3.4 ¶1)说:

realloc 函数释放ptr 指向的旧对象,并返回一个指向具有size 指定大小的新对象的指针。

并且没有给出size==0 的“特殊情况”;所以,你得到一个指向一个大小为零的对象的指针——但它可能仍然是一个对象,并且仍然必须被释放。

有趣的是,我认为realloc 在这种情况下可能会失败,返回NULL;在这种情况下,在您的代码中内存泄漏了,因为当realloc 失败时,它不会释放您传递给它的原始内存块(这就是为什么您从不执行array = realloc(array, size) 但您总是使用中间变量来检查 NULL 以避免内存泄漏)。

实际上,标准确实为所有内存分配函数指定了size==0 实现定义的行为,而不仅仅是我记忆中的malloc;因此,行为是由实现定义的,如下所述:

更直观地说,realloc 在“概念上等同于”malloc+memcpy+free 在另一个指针上,malloc-ing 一个 0 字节的内存块返回 NULL要么是唯一的指针,不用于存储任何东西(你要求 0 字节),但仍然是freeed。所以,不,不要像那样使用realloc,它可能适用于某些实现(即 Linux),但肯定不能保证。

此外,尚不清楚您是如何推断出free 不起作用的。我可以想到两种可能让您确信这一点的方法:

    array 的值和它指向的数据没有变化; 任务管理器中分配的内存/top/whatever 没有减少。

对于第一种情况,这是正常的;当你释放一个指针时,它不会神奇地被擦除——你的指针仍然指向它所指向的位置,但该内存不再是你的——它现在回到了 C 运行时,它可能会在未来重新放弃它malloc。这就是为什么那个东西被称为“悬空指针”的原因,很多人在free之后将其设置为NULL,以避免在已经释放的内存空间中再次写入。

至于第二个,分配器通常不会立即将内存归还给操作系统(除非我们谈论的是非常大的块);这个想法可能是应用程序很快就会再次需要这样的内存,并且为当前进程保留该内存可以避免连续的系统调用以从操作系统获取/提供内存。由于用于监视正常使用的内存的系统实用程序只能查看操作系统为进程提供的内容,因此它们没有显示任何内存使用量减少是正常的。

顺便说一句,请记住,如果您的 char ** array 包含指向使用 malloc 分配的内容的指针,您必须首先使用 free 它们,否则您会泄漏内存。


第二个问题:

C 字符串是以 null 结尾的,即字符串的最后一个字符始终是 \0 以标记字符串结束,而 strlen 为您提供字符串的长度,不包括 null 终止符。因此,如果您不添加 +1,则分配的 char 比实际存储字符串所需的内存少。


附录

不起作用我的意思是它确实崩溃了(问题可能出在其他地方)但是当我将它更改为 realloc(X, 0) 时它确实起作用了,在删除使用的动态已用内存的意义上

正如手册页所说,

malloc()、calloc()、realloc() 或 free() 中的崩溃几乎总是与堆损坏有关,例如溢出分配的块或释放同一指针两次。

您的代码中可能还有其他一些错误,但如果没有看到它,就不可能知道哪里/哪里出了问题。

【讨论】:

标准确实提供了 size==0 的特殊情况,只是所有内存管理功能都相同。在 n1256 草案中,它位于第 7.20.3.1 节 @Andrei:你是对的,幸运的是,我已经说过malloc :) 修复答案...【参考方案4】:

关于第二个问题:

在 C 中,字符串应该以空字符 '\0' 结束。当你使用strlen时,这个字符不计算在内,但你需要为它分配足够的空间(即+1)。

如果您尝试打印不包含空字符的字符串,可能会打印一些“随机”字符,但也可能会崩溃。 如果你使用 strcpy,你会做一个缓冲区溢出。

【讨论】:

好的,谢谢,我自己应该明白这一点,因为在测试中我们寻找那种字符串......那是我的错,我的问题是我开始写 +8,然后 +9 ,因为我认为这是位数的问题。现在我相信我明白了。【参考方案5】:

如果您希望保持兼容性,realloc(p,0) 永远不会等同于free(p),并且没有后续释放的零分配是简单明了的内存泄漏,即使在 Linux 上也是如此。

/* leak.c */
#include <mcheck.h>
#include <stdlib.h>
int main(int argc, char* argv[]) 
    void* p;
    mtrace();
    p = malloc(0x100);
    p = realloc(p, 0);
    exit(EXIT_SUCCESS);


$ cc -g leak.c -o leak
$ export MALLOC_TRACE=/tmp/t
$ ./leak
$ mtrace ./leak $MALLOC_TRACE

【讨论】:

【参考方案6】:

问题出在 char **array !! 那是怎么建的? 听起来它不是一个连续的内存块,它的构建方式如下:

char **array  = malloc(sizeof(char*)*size);
for (size_t i=0; i< size; i++) 
    array[i] = malloc(strlen(string)+1);

如果是这种情况,那么每个数组索引都有其指向随机内存块的单独指针! 在这种情况下,在清理数组之前,您需要释放每个单独的索引!!!

for (size_t i=0; i<size; i++) 
    free(array[i]);

free(array);

同样使用 realloc,NULL 也可能意味着失败,因此您正在悬挂旧指针! 从不做

foo= realloc(foo, new_size);

if (tmp = realloc(foo, new_size) ) 
     foo = tmp;
 else 
   // clean your ram, how do you handle failure?
   free(foo);foo = NULL;
   // or you're ok without the new size,or try to realloc again
   // up to you

【讨论】:

以上是关于使用 realloc (X, 0) 代替 free() 并使用字符串长度为 +1 的 malloc的主要内容,如果未能解决你的问题,请参考以下文章

动态内存函数+经典笔试题@动态内存管理---malloc +free + calloc + realloc

零基础学C语言内存知识总结:realloc函数和free函数

梦开始的地方—— C语言动态内存管理(malloc+calloc+realloc+free)

堆的分配和释放(malloc,free,calloc,realloc)

C语言,realloc动态内存申请,出现报错double free or corruption (!prev)

C:malloc/calloc/realloc/alloca内存分配函数