为什么我使用realloc()获得双重错误?

我试过在C中编写一个字符串替换函数,它使用 malloc()分配的 char * 。它有点不同,它会查找和替换字符串,而不是起始字符串中的字符。

如果搜索字符串和替换字符串长度相同(或者替换字符串比搜索字符串短),这样做很简单,因为我已分配了足够的空间。如果我尝试使用 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 
#include 
#include 

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");
    }
}
0
额外 编辑
意见: 2

12 答案

您可以购买具有串行接口的WiFi模块,但它们是一点点pricy。基本上你可以通过UART来控制它们,并且向它们发送类似于你在过去几天控制拨号调制解调器的命令。

WiFi module

5
额外
这是一个好主意,我甚至在它上面找到了一个可以让集成更容易的突破板。我唯一的预订是电力需求和规模。感谢您的建议!我一定会记住这为未来的项目:-)
额外 作者 Mario Marinato,

扩大一点。

戴尔建议我也会建议。 Roving Networks还生产一套完整的BT模块。

扩大。 ZigBee 是完全不受Wi-Fi限制的独立协议。 ZigBee在辐射功率方面与BT类似,这与量程有很大的关系,但不同之处在于它允许多个节点创建一个网络,每个节点通过范围有效地扩展网络。我希望这清楚,他们是相似的,但不相关。

3
额外
谢谢(你的)信息。帮助澄清我的一些问题:)
额外 作者 Mario Marinato,

只是想与大家分享我目前的解决方案:

在与LittleBirdElectronics的Marcus和Madeleine聊天之后,我们提出了以下可能的解决方案:

1)Xily在lilypad上,

2)USB Arduino,Ethernet Sheild和Xbee组合将消息传输到WWW。

这只是许多可能的解决方案之一,但对我来说,这似乎通过使用xbee而不是直接在lilypad上使用802.11,为lilypad端提供了低功耗和大小要求的好处。

另外,我已经有了一个备用的Arduino和以太网盾牌,并且还想借助xbee玩一个借口! ;-)

非常感谢您的意见!这在设计解决方案时非常有用。

3
额外
我们考虑了这两个问题,但我们发现的模块昂贵且在海外。鉴于我已经有一个Arduino和一个以太网盾,这变得更简单了:-)非常感谢您的建议!
额外 作者 Mario Marinato,
我认为我提出的解决方案会更便宜。但是,也许你更坚实,更紧凑,更容易构建。你已经有硬件了。
额外 作者 Chris Bunch,

我可以使用XBee模块并以某种方式与我的家庭路由器连接吗?

是的,但您需要将XBee模块连接到您的路由器。如果您的路由器有USB端口,您可以尝试在路由器主板上找到串行端口,或通过USB到串行适配器。你的路由器也应该在Linux下工作(可能是openWRT)。

1
额外

作为一般规则,您应该永不在用户提供的缓冲区上执行free或realloc操作。您不知道用户在哪里分配空间(在您的模块中,在另一个DLL中),因此您无法使用用户缓冲区上的任何分配功能。

假设你现在不能在你的函数中做任何重新分配,你应该改变它的行为一点,比如只做一次替换,这样用户将能够计算出结果字符串的最大长度,并为你提供一个足够长的缓冲区更换发生。

然后,您可以创建另一个函数来执行多个替换,但您必须为结果字符串分配整个空间并复制用户输入字符串。然后你必须提供一种方法来删除你分配的字符串。

导致:

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

我的快速提示。

Instead of:
void strrep(char *input, char *search, char *replace)
try:
void strrep(char *&input, char *search, char *replace)

and than in the body:
input = realloc(input, strlen(input) + delta);

一般阅读关于传递函数参数作为值/参考和realloc()描述:)。

0
额外
符号 void strrep(char *&input,char * search,char * replace)在C中无效吗?尽管它在C ++中有效。问题不是,AFAICT从来没有用C ++标记过。在最好的情况下,代码应该是 void strrep(char ** input,char * search,char * replace),尽管很容易争辩 char * strrep(const char * const char * search,const char * replace)是一个可用的接口(输入字符串不会改变;修改过的字符串会被分配并返回)。
额外 作者 Jonathan Leffler,

请注意,尝试编辑您的代码以摆脱html转义代码。

好吧,尽管自从我使用C / C ++以来已经有一段时间了,但是如果在原始块之后存在内存空间,增长的realloc只会重用内存指针值。

例如,考虑这一点:

(XXXXXXXXXX ..........)

如果你的指针指向第一个x和。意味着可用内存位置,并且您将变量指向的内存大小增加5个字节,它会成功。这当然是一个简单的例子,因为块被四舍五入到一定的大小以便对齐,但无论如何。

但是,如果随后尝试将其增加10个字节,并且只有5个可用,则需要将该块移到内存中并更新指针。

然而,在你的例子中,你传递的是一个指向字符的指针,而不是指向你的变量的指针,因此当内部的strrep函数可以调整正在使用的变量时,它是strrep函数的局部变量,您的调用代码将保留原始指针变量值。

但是,该指针值已被释放。

在你的情况下,输入是罪魁祸首。

但是,我会提出另一个建议。在你的情况下,它看起来像 input 变量确实是输入的,如果是的话,它根本不应该被修改。

因此,我会尝试找到另一种方法来做你想做的事情,而不用改变 input ,因为这样的副作用很难追踪。

0
额外

这似乎工作;

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;
}

叹息,无论如何发表代码没有它吸吮?

0
额外
在评论可用之前添加评论,因为评论被写为答案:这似乎只是改变了第一次发生。这可能是合理的,因为我没有真正说明它必须改变它们全部!
额外 作者 Matthew Schinckel,

其他人对派对迟到表示歉意 - 两个半月前。哦,我花了很多时间做软件考古。

我感兴趣的是,没有人明确地评论过原始设计中的内存泄漏问题,还是错误的。它正在观察内存泄漏情况,确切地告诉我为什么你会得到双重释放的错误(因为,确切地说,你多次释放相同的内存 - 并且在践踏已经释放的内存后这样做)。

在进行分析之前,我会同意那些说你的界面不如恒星的人;但是,如果您处理内存泄漏/践踏问题并记录了“必须分配内存”的要求,则可能是“确定”。

有什么问题?那么,您将缓冲区传递给realloc(),并且realloc()会返回一个指向您应该使用的区域的新指针 - 并且忽略该返回值。因此,realloc()可能已经释放了原始内存,然后再次将它传递给同一个指针,并且它抱怨说你将两次释放相同的内存,因为你再次将原始值传递给它。这不仅会泄漏记忆,而且意味着你将继续使用原来的空间 - 而John Downey在黑暗中的镜头指出你滥用了realloc(),但并未强调你的行为有多严重。还有一个错误的错误,因为你没有为NUL'\ 0'分配足够的空间来终止字符串。

内存泄漏是因为你没有提供一种机制来告诉调用者关于字符串的最后一个值。因为你不停地践踏原始字符串加上后面的空格,它看起来像代码工作,但如果你的调用代码释放空间,它也会得到一个双免费错误,或者它可能会得到一个核心转储或等价物,因为存储器控制信息被完全加扰。

您的代码也无法防止无限增长 - 考虑用'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的“编写固体代码”一书。

0
额外
谢谢,这是对我做错事情的一个很好的分析(并且双免费在某种意义上是我做错的几件事的副产品)。我想我的脑海里已经有了realloc( )只是扩展了内存分配 - 当我思考时,这根本就没有意义!
额外 作者 Matthew Schinckel,

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

我见过代码在哪里

realloc(bytes, smallerSize);

被用来调整缓冲区的大小,使其变小。工作了大约一百万次,然后出于某种原因,realloc决定即使你缩短了缓冲区,它也会给你一个不错的新副本。所以在发生不好的事情之后,你每秒钟在随机的地方崩溃。

始终使用realloc的返回值。

0
额外

只是在黑暗中拍摄,因为我还没有尝试过,但是当你重新分配它时,它就像malloc一样返回指针。因为realloc可以根据需要移动指针,所以如果您不执行以下操作,则很可能会对无效指针进行操作:

input = realloc(input, strlen(input) + delta);
0
额外
如果realloc失败,它将返回NULL,并保留现有的缓冲区。你刚刚失去了指针...... :-(
额外 作者 Roger Lipscombe,

首先,对不起,我迟到了派对。这是我的第一个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()一个新的缓冲区,返回它,并让调用者决定是否释放旧文本。这使得调用者保留原始字符串的额外好处!

0
额外