在不造成内存泄漏的情况下替换函数中的char*

6
我正在尝试制作一个查找和替换的功能,它可以正常工作,但是valgrind报告了巨大的内存泄漏。
看一下代码:
void repl(char** str) {

    // build a new string (simulating find&replace)

    char* replacement = (char*)malloc(7);
    strcpy(replacement, "my ass");

    // this causes "free(): invalid pointer" crash
    //free(*str);

    *str = replacement; // return to caller
}

/* main function */
int main (int argc, char **argv) {

    // out original string
    char* str = "memory leak here";
    repl(&str); // replace something 1st time
    repl(&str); // 2nd time
    repl(&str); // 3rd time

    printf("%s\n",str); // look at output

    exit(0);
}

当“免费”到位时,您将获得以下内容:
*** glibc detected *** ./test: free(): invalid pointer: 0x0000000000401013 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7f264e7ecb96]
....  

如果没有free,会出现内存泄漏(valgrind输出):

==26236== LEAK SUMMARY:
==26236==    definitely lost: 14 bytes in 2 blocks
==26236==    indirectly lost: 0 bytes in 0 blocks
==26236==      possibly lost: 0 bytes in 0 blocks
==26236==    still reachable: 7 bytes in 1 blocks
==26236==         suppressed: 0 bytes in 0 blocks

我对指针还不是很熟悉,请耐心等待。 也许这不是最好的方法,所以请建议更好的查找和替换方法,以避免内存泄漏。

我尝试只返回结果,但是同样出现了内存泄漏。


+1 很好的问题,这表明你正在尝试自己解决它。 - ciphermagi
您是否希望有一个智能化的函数,它能够删除未分配的字符串(例如您的第一种情况 const 字符串)并且能够正确释放以前调用时动态分配的字符串?我这样问是因为如果这是您的意图,那么问题不在于需要一个函数。问题在于首先期望函数为您完成这项任务。(很好,您在此使用了 valgrind;对于大多数初学 C 语言和指针的人来说,您已经领先于他们了)。 - WhozCraig
看看这个:http://forum.codecall.net/topic/51010-dynamic-arrays-using-malloc-and-realloc/ - ciphermagi
5个回答

4

glibc是什么?

glibc诊断与以下事实有关:str并非始终指向由malloc分配的内存段之一,这是free的要求之一。换句话说,如果您第一次调用repl并且str指向未经过malloc分配的内存段,则会导致free行为异常。


valgrind是什么?

valgrind诊断存在的原因是由于在没有(当前故障)使用free的情况下,由repl内部的malloc分配的内存永远不会被释放,因此您正在泄漏内存。


建议的解决方案

我认为最好用语言来解释它,而不是提供一个可运行的实现,因为您似乎渴望通过实践学习。

  1. 不要接受指向指针的指针,而是接受指向字符的指针,并返回指向传入字符串的修改版本的新内存段的指针。该新内存段已通过malloc分配,

  2. 重命名函数以明确它正在分配需要释放的内存,

  3. 记录您的函数,以便调用者知道它负责释放内存,而不是您的替换函数。


我明白了。那么我该如何检查传入的字符串是否是通过malloc分配的呢? - MightyPork
1
@MightyPork:你不能这样做,而且你也不想这样做。 - Ed S.
好的,那么有什么建议来解决我的问题吗?现在我知道了问题出在哪里,但是没有办法修复它。 - MightyPork
好的解决方案。虽然对调用者有些额外的努力,但在正确执行时它是有效的。 - MightyPork
你怎么决定在替换后是否要释放指针?难道你会让调用者负责吗?你能想象需要多么丑陋的代码吗? - Sergey Kalinichenko
显示剩余6条评论

3
无法删除/释放“memory leak here”字符串,因为它实际上不存在需要释放的内存中,它是程序代码中的常量字符序列,这就是为什么你会因为无效指针而获得运行时错误。当你移除free操作时,丢失的14个字节是因为你分配了3次7个字节,但只有最后一个可以访问。

2
malloc 了什么,就要用 free 来释放。如果使用 free 释放未被 mallocrealloc 动态分配的内存,将会导致未定义的行为。这就是为什么你第一次调用 free 时出现错误的原因;str 指向的内容没有被动态分配。

这似乎也是一个设计不良的地方。该函数以缓冲区作为输入。除了可以写入之外,它不应该对这个缓冲区做任何假设。它不应该在其输入上调用 free;确保内存清理是调用者的工作。

同样地,你甚至不需要使用 malloc。你可以直接写入缓冲区并强制用户分配它(这更加规范)。在需要返回动态分配内存的情况下,你应该记录返回的内存必须由调用者释放。你的模型必须在所有权方面保持一致。


问题在于我事先不知道缓冲区需要多长。然而,基于您的第一个建议(将free留给调用者),我认为已经解决了这个问题。尽管还需要进一步测试。 (再次说一遍,调用者假定返回的字符串是malloc分配的,设计很差,对吧?) - MightyPork
@MightyPork:如果返回的字符串可以是任何大小,那么可能只有返回动态分配的字符串才能实现。这没关系,有一些C库函数也可以做同样的事情。只要有文件记录说明调用者拥有该内存,就可以了。 - Ed S.

2

这是与你传递给repl函数的字符串所有权有关的问题:一旦你这样做,

*str = replacement;

*str所指向的内存已经永久消失,成为了内存泄漏。因此,在重新分配之前需要释放它。

但这还不是全部!这意味着您不能将字符串常量或在静态或自动存储中分配的char数组传递给repl,因为它们不能被释放。因此,在将其传递给repl之前,您需要复制原始字符串:

void repl(char** str) {
    char* replacement = malloc(7);
    strcpy(replacement, "my ass");
    // This will no longer be a problem - see the change in main()
    free(*str);
    *str = replacement; // return to caller
}

/* main function */
int main (int argc, char **argv) {
    // Now that the original string is malloced,
    // free() inside repl() is no longer a problem:
    char* str = malloc(17);
    strcpy(str, "memory leak here");
    repl(&str); // replace something 1st time
    repl(&str); // 2nd time
    repl(&str); // 3rd time
    printf("%s\n",str); // look at output
    exit(0);
}

请注意,在实际的repl中,使用realloc可能会更好 - 它有可能加速小替换的速度。

好的,但是如果没有验证的方式,_要求_输入字符串被分配内存空间,这不是一个好主意吗? - MightyPork
-1:@MightyPork 是的,这是一个不好的想法,通常不建议这样做。 - Filip Roséen - refp
@MightyPork 不幸的是,你没有其他方法可以做到这一点:绝对、肯定地,没有办法找出一个指针是来自堆、栈还是静态存储,除非采用某种黑客手段。调用者也不可能对释放该内存负责,因为他不知道是否需要使用 free() - Sergey Kalinichenko
@FilipRoséen-refp 不仅这不是一个坏主意,而且这也是唯一的出路。注意:对竞争答案进行投票反对是非常不好的行为。 - Sergey Kalinichenko
@dasblinkenlight,我不是因为它是一个竞争性的答案而对其进行投票,而是因为我发现这个答案具有误导性/不正确。这不是唯一的方法,但我现在看到OP的问题不涉及传递给函数的指针的先决条件和函数的使用方式...因此我将取消之前的取消投票 - Filip Roséen - refp
@FilipRoséen-refp,你认为这个答案哪里“误导/不正确”了? - Sergey Kalinichenko

0

在调用者中添加Free

/* main function */

int main (int argc, char **argv) {

    // out original string
    char* str = "memory leak here";
    repl(&str); // replace something 1st time
    free(str);
    repl(&str); // 2nd time
    free(str);
    repl(&str); // 3rd time

    printf("%s\n",str); // look at output
    free(str);
    exit(0);
}

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