【问题标题】:Freeing up memory outside of the function causes a double free or corruption error在函数之外释放内存会导致双重释放或损坏错误
【发布时间】:2016-11-06 10:52:32
【问题描述】:

我遇到了一个问题,当我尝试在分配该内存块的函数之外释放内存时出现双重释放或损坏错误,即使我已将指针传递给内存块指向该函数之外的指针。

我的代码是这样的,在 main() 函数中,我调用了一个定义为 char * reverseComplement(char * pattern); 的函数:

char * rev = reverseComplement(dna_input);

其中dna_input 是一个char 指针,指向在main() 中分配的已分配内存块。在reverseComplement() 函数中,有一行我在其中分配内存,然后在最后返回指向该内存块的指针。

...
...
char * revcomplpattern = (char *)malloc(strlen(pattern));
...
...
return revcomplpattern;

我认为可能导致问题的一个原因是,在 reverseComplement() 函数完成执行后,它的堆栈被拆除,因此我将无法访问堆上的内存。但这不应该是因为我将堆分配内存的句柄从位于reverseComplement()revcomplpattern 传递到位于main()rev。所以main() 中的free(rev) 应该可以完成这项工作。

我不知道我在这里可能做错了什么,非常感谢任何帮助!

【问题讨论】:

  • malloc(strlen(pattern)) --> malloc(strlen(pattern)+1)
  • 在函数中分配内存不是最好的习惯,它们使调用者有责任调用free()
  • 首先,删除演员表,然后看看编译器告诉你什么。
  • 到目前为止,您所描述的内容没有任何问题(尽管@BLUEPIXY 可能发现了第二个问题)。解决此类问题的一种方法是反复减少代码,将其缩小到最小的可重现情况,以便找到触发问题的代码行。这应该让你更接近于弄清楚发生了什么(或者至少能够问下一个问题)。在创建 StackOverflow 之前,我们就是这样解决此类问题的 ;-) 祝你好运!
  • malloc 之后看不到对 NULL 的检查。 strdup 也会更有表现力

标签: c function pointers memory memory-management


【解决方案1】:

也许您在将控制权返回给 main() 之前破坏了内存?释放您的“有效”内存块可能会失败,因为您的 malloc 堆已经损坏。或者,可能 reverseComplement() 中的一些错误代码路径已经释放了这个内存?

【讨论】:

  • 也许,......也许......”,也许这个答案更适合作为评论。
【解决方案2】:

...其中 dna_input 是指向在 main() 中分配的已分配内存块的 char 指针。在 reverseComplement() 函数中,有一行我分配内存,然后在最后返回指向该内存块的指针。

您的想法告诉我,您正在分配内存并以某种方式在 reverseComplement() 函数中使用该内存,但在该函数中,您正在分配另一块内存。我不知道为什么。

...我得到双重释放或损坏错误的问题...

您需要确保在程序出于任何原因退出时释放程序中所有已分配的内存块。如果没有,那么您将有内存泄漏。

如果您必须在函数中分配内存(我不推荐),那么您可以使用如下代码:

char *getnewblock(int size)
{
    char *secondblock=malloc(size);
    return secondblock;
}

int main()
{
    char *myblock = malloc(1000);
    char *myblock2 = getnewblock(5000);
    free(myblock);
    free(myblock2);
    return 0;
}

上面的程序分配了 1000 字节的内存,然后调用一个函数来分配 5000 字节的内存,然后释放两个内存块。

我真正建议您只在一个函数中分配内存,然后在子函数中使用相同的内存块,如下所示:

void insertfirstchar(char *block,char onechar)
{
   block[0]=onechar;
}

int main()
{
    char *myblock = malloc(1000);
    insertfirstchar(myblock,'A');
    free(myblock);
    return 0;
}

在上面的程序中,分配了 1000 个字节,然后调用一个函数将内存块的第一个字节设为字母“A”,然后释放内存。最后一个程序可以作为您的起点。

【讨论】:

  • 是的,我也想这样做,但这会让我的 reverseComplement() 函数必须接受一个我不想这样做的额外参数。不过现在我很好奇,在专业项目中,他们是否遵循您建议的做法,还是可以接受我的风格?
【解决方案3】:

好吧,伙计们,我刚刚弄清楚了这一切!很抱歉给您带来麻烦!事实证明,错误不是因为我释放分配的内存的方式,而是因为malloc() 调用。

原来我分配的时候做了什么

char * revcomplpattern = (char *)malloc(strlen(pattern));

紧随其后的是这些行

int strend = strlen(pattern)/sizeof(char) -1 ;
revcomplpattern[strend+1] = '\0';

我的目标是确保 revcomplpattern 在我在其末尾插入一个空终止符时会变成一个字符串。事实证明,在索引[strend+1] 时,我实际上是在触摸内存中未分配的位置!在更正 malloc() 调用后,为多一个字符增加了一个空格

char * revcomplpattern = (char *)malloc(strlen(pattern) + 1 * sizeof(char));

free(rev) 的调用不会引发错误!现在一切正常,Valgrind 没有显示错误!特别感谢@tofro 和@jdigital 为您提供建议。当我使用 valgrind 时,它开始告诉我 strlenmalloc 有一个错误,直到我将其缩小到那条线并仔细查看并意识到出了什么问题,我一开始无法理解。

伙计,我觉得 GCC 提供了非常神秘的错误消息,但没有多大帮助。我现在真的很困惑,如果有人能回答,为什么我的错误没有抛出 SEGFAULT 呢?我认为这会导致段错误,因为我正在触摸未分配的内存部分???

不过还是感谢大家的帮助!

【讨论】:

  • sizeof(char) 根据定义等于1。如果不是这个malloc(strlen(pattern) + 1 * sizeof(char)); 会失败,因为它应该是malloc((strlen(pattern) + 1) * sizeof(char));
  • 而且,如果这真的 C 代码只需将所有这些转换删除到malloc(),因为它不是必需的,也不推荐任何方式。这与 C++ 不同。
  • 最后:写出 C 中有效内存的界限会导致 Undefined Behaviour。从这一刻起,anything 可能会发生,“anything”是一个分段违规异常,或者什么都没有,或者...或...
  • @alk 谢谢你指出应该是malloc((strlen(pattern) + 1) * sizeof(char));!但是,如果说sizeof(char) 不是1,那么它会更大一些,比如4,那么没有什么坏处吧?只是我分配的空间比我需要的多吗?
  • @alk 注意到malloc() 的选角。这是我在整个 stackoverflow 的许多其他讨论中看到的。但有趣的是,这就是我们在学校被教导的方式!这就是他们在我们的编程和计算系统入门课程中所教授的内容。投射 malloc() 有什么问题?
猜你喜欢
  • 2021-12-04
  • 1970-01-01
  • 2013-12-04
  • 1970-01-01
  • 1970-01-01
  • 2011-03-12
  • 1970-01-01
  • 2011-11-18
  • 2021-08-26
相关资源
最近更新 更多