为什么我用realloc()获得一个双免费的错误?

vvelit 发布于 2018-02-06 c 最后更新 2018-02-06 01:04 520 浏览

我试图在C中编写一个字符串替换函数,该函数在char *上工作,PLACEHOLDER_FOR_CODE_0已经使用malloc()分配。它有点不同,它会查找和替换字符串,而不是起始字符串中的字符。 如果搜索和替换字符串的长度相同(或者替换字符串比搜索字符串短),这样做是微不足道的,因为我已经分配了足够的空间。如果我尝试使用realloc(),我得到一个错误,告诉我我正在做一个双免费 - 我不明白我是如何,因为我只使用realloc()。 也许有一些代码会有所帮助:

void strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;
while (find = strstr(find, search)) {
if (delta > 0) {
            realloc(input, strlen(input) + delta);
            find = strstr(input, search);            
        }
memmove(find + replaceLen, find + searchLen, strlen(input) - (find - input));
        memmove(find, replace, replaceLen);
    }
}
该程序工作,直到我尝试realloc()在被替换的字符串将比初始字符串更长的实例。 (它仍然是一种工作,它只是吐出错误以及结果)。 如果有帮助,调用代码如下所示:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void strrep(char *input, char *search, char *replace);
int main(void) {
    char *input = malloc(81);
while ((fgets(input, 81, stdin)) != NULL) {
        strrep(input, "Noel", "Christmas");
    }
}
已邀请:

aet

赞同来自:

只是在黑暗中的一个镜头,因为我还没有尝试过,但是当你重新分配它返回指针很像malloc。因为realloc可以根据需要移动指针,所以如果不执行以下操作,很可能是在无效指针上操作:

input = realloc(input, strlen(input) + delta);

oad

赞同来自:

请注意,请尝试编辑您的代码以摆脱html转义代码。 那么,虽然自从我使用C/C++以来已经有一段时间了,但是如果在原始块之后有内存空间的话,realloc只会重用内存指针值。 例如,考虑一下: (XXXXXXXXXX ..........) 如果你的指针指向第一个x和。意味着可用内存位置,并且将变量指向的内存大小增加5个字节,则会成功。这当然是一个简单的例子,因为块被四舍五入到一定的大小以便对齐,但无论如何。 但是,如果随后尝试将其增加10个字节,并且只有5个可用,则需要将该块移到内存中并更新指针。 然而,在你的例子中,你传递的是一个指向字符的指针,而不是指向你的变量的指针,因此当内部的strrep函数可以调整正在使用的变量时,它是strrep函数的局部变量,您的调用代码将保留原始指针变量值。 但是,这个指针值已经被释放了。 在你的情况下,输入是罪魁祸首。 但是,我会提出另一个建议。在你的情况下,它看起来像 input 变量确实是输入的,如果是的话,它根本不应该被修改。 因此,我会试着找到另一种方法来做你想做的事情,而不用改变 input ,因为这样的副作用很难被追踪。

xesse

赞同来自:

作为一般规则,您应该从不 在用户提供的缓冲区上执行free或realloc操作。你不知道用户在哪里分配空间(在你的模块中,在另一个DLL中),所以你不能在用户缓冲区中使用任何分配函数。 假设你现在不能在你的函数中做任何重新分配,你应该改变它的行为,比如只做一个替换,所以用户将能够计算出结果字符串的最大长度,并为你提供足够长的缓冲区更换发生。 然后,您可以创建另一个函数来执行多个替换,但是您将不得不为分配的字符串分配整个空间并复制用户输入字符串。那么你必须提供一种方法来删除你分配的字符串。 导致:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);

nodio

赞同来自:

我的快速提示。 而不是:
void strrep(char input, char search, char replace)结果 尝试:点击 void strrep(char &input, char search, char replace) 而不是在身体上:
input = realloc(input, strlen(input) + delta); 一般阅读关于传递函数参数作为值/参考和realloc()描述:)。

tfugit

赞同来自:

这似乎工作;

char *strrep(char *string, const char *search, const char *replace) {
    char *p = strstr(string, search);
if (p) {
        int occurrence = p - string;
        int stringlength = strlen(string);
        int searchlength = strlen(search);
        int replacelength = strlen(replace);
if (replacelength > searchlength) {
            string = (char *) realloc(string, strlen(string) 
                + replacelength - searchlength + 1);
        }
if (replacelength != searchlength) {
            memmove(string + occurrence + replacelength, 
                        string + occurrence + searchlength, 
                        stringlength - occurrence - searchlength + 1);
        }
strncpy(string + occurrence, replace, replacelength);
    }
return string;
}
唉,反正有没有它吮吸邮政编码?

kodit

赞同来自:

首先,对不起,我迟到了。这是我的第一个stackoverflow答案。 :) 正如已经指出的那样,当realloc()被调用时,你可以改变指针到被重新分配的内存。发生这种情况时,参数“字符串”变为无效。即使您重新分配,一旦功能结束,更改就会超出范围。 为了回答OP,realloc()返回一个指向新重新分配的内存的指针。返回值需要存储在某个地方。一般来说,你会这样做:

data *foo = malloc(SIZE * sizeof(data));
data *bar = realloc(foo, NEWSIZE * sizeof(data));
/* Test bar for safety before blowing away foo */
if (bar != NULL)
{
   foo = bar;
   bar = NULL;
}
else
{
   fprintf(stderr, "Crap. Memory error.\n");
   free(foo);
   exit(-1);
}
正如TyBoer指出的那样,你们不能改变传入的指针的值作为这个函数的输入。你可以指定任何你想要的,但是这个改变将在函数结束时超出范围。在下面的块中,一旦函数完成,“输入”可能是也可能不是无效指针:
void foobar(char *input, int newlength)
{
   /* Here, I ignore my own advice to save space. Check your return values! */
   input = realloc(input, newlength * sizeof(char));
}
Mark试图通过返回新指针作为函数的输出来解决这个问题。如果你这样做,主叫方的责任就是不再使用他用来输入的指针。如果它匹配返回值,那么你有两个指向同一地点的指针,只需要在其中一个地方调用free()。如果它们不匹配,则输入指针现在指向可能或可能不属于进程的内存。解除引用可能会导致分段错误。 您可以使用双指针来输入,如下所示:
void foobar(char **input, int newlength)
{
   *input = realloc(*input, newlength * sizeof(char));
}
如果调用者在某个地方有一个输入指针的重复,那么这个重复仍然可能是无效的。 我认为这里最干净的解决方案是在尝试修改函数调用者的输入时避免使用realloc()。只要malloc()一个新的缓冲区,返回,并让调用者决定是否释放旧文本。这使得调用者保留原始字符串的附加好处!

gnihil

赞同来自:

其他人因为迟到了这个派对而道歉 - 两个半月前。哦,我花了很多时间做软件考古。 我感兴趣的是,没有人明确地评论过原始设计中的内存泄漏,还是错误的错误。而且它正在观察内存泄漏情况,确切地告诉我为什么你会得到双倍的错误(因为,确切地说,你多次释放相同的内存 - 而且你是在践踏已经释放的内存之后这样做的)。 在进行分析之前,我会同意那些说你的界面不如恒星的人;但是,如果处理内存泄漏/践踏问题并记录“必须分配内存”的要求,则可能是“OK”。 有什么问题?那么,您将缓冲区传递给realloc(),并且realloc()将返回一个指向您应该使用的区域的新指针 - 并且忽略该返回值。因此,realloc()可能已经释放了原来的内存,然后再次传递给它同一个指针,并且它抱怨说你将两次释放相同的内存,因为你再次将原始值传递给它。这不仅会泄漏记忆,而且意味着你将继续使用原来的空间 - 而约翰·唐尼在黑暗中的枪声指出你滥用了realloc(),但是并没有强调你的行为有多严重。还有一个错误的错误,因为你不为NUL'\ 0'分配足够的空间来终止字符串。 发生内存泄漏是因为您没有提供一种机制来告诉调用者有关字符串的最后一个值。因为你不停地践踏原来的字符串加上后面的空格,所以看起来代码起作用了,但是如果你的调用代码释放了空间,它也会得到一个double-free的错误,或者它可能得到一个核心转储或者等价物,存储器控制信息被完全加扰。 您的代码也无法防止无限期增长 - 考虑用“Joyeux Noel”替换“Noel”。每一次,你会添加7个字符,但你会在被替换的文本中找到另一个Noel,并展开它,等等等等。我的fixup(下)不解决这个问题 - 简单的解决方案可能是检查搜索字符串是否出现在替换字符串;另一种方法是跳过替换字符串,然后继续搜索。第二个有一些不平凡的编码问题要解决。 所以,我建议你的被调用函数的修改是:

char *strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;
while ((find = strstr(find, search)) != 0) {
        if (delta > 0) {
            input = realloc(input, strlen(input) + delta + 1);
            find = strstr(input, search);            
        }
memmove(find + replaceLen, find + searchLen, strlen(input) + 1 - (find - input));
        memmove(find, replace, replaceLen);
    }
return(input);
}
此代码不检测内存分配错误 - 如果realloc()失败,可能会崩溃(但是,如果没有,泄漏内存)。有关内存管理问题的广泛讨论,请参阅Steve Maguire的“编写固体代码”一书。

et_et

赞同来自:

realloc很奇怪,很复杂,只能在每秒处理大量内存的时候使用。即 - 它实际上使您的代码更快。 我见过代码在哪里

realloc(bytes, smallerSize);
被用来调整缓冲区的大小,使其变小。工作了大约一百万次,然后由于某种原因,realloc认为,即使你缩短了缓冲区,它会给你一个不错的新副本。所以在发生不好的事情之后,你每秒钟在一个随机的地方崩溃。 总是使用realloc的返回值。