字符串重新分配的意外行为

时间:2019-11-29 21:22:29

标签: c pointers malloc free realloc

我遇到了带有字符串函数的内存分配的奇怪行为。

注意:现在,我被告知要忽略分配操作的失败。

我的代码是:

void string_reallocation(char *result, int result_length) {
    char *temp_result = malloc((strlen(result) + 1) * sizeof(char));

    strcpy(temp_result, result);

    realloc(result, (result_length + 1) * sizeof(char));

    strcpy(result, temp_result);

    free(temp_result);
}

此函数在while循环内通过迭代调用:

    while (current_node != NULL) {
        current_value_to_string = current_node->toStringFunc(current_node->value);
        current_value_length = (int) strlen(current_value_to_string);
        current_length += current_value_length + arrow_length;

        string_reallocation(result, current_length);

        strcat(result, current_value_to_string);
        strcat(result, arrow);

        current_node = current_node->next;
    }

current_node的节点类型如下:

typedef struct t_node {
    Element value;
    struct t_node *next;
    elementDestroy destroyFunc;
    elementCopy copyFunc;
    elementToString toStringFunc;
} *Node;

出于某种原因,尤其是在第三次迭代中,free(temp_result);因分段错误而失败。

我不认为while循环与分段错误有任何关系,但我将其放在此处以防万一。

2 个答案:

答案 0 :(得分:2)

这是一个双相解决方案,因为您需要通过检查realloc()的原型来了解其用法。让我们先做吧。

更改此:

realloc(result, (result_length + 1) * sizeof(char));

对此:

result = realloc(result, (result_length + 1) * sizeof(char));

reference起,我们获得了该方法的原型:

  

返回值:指向重新分配的内存块的指针,该指针可能是   与ptr相同或位于新位置。


现在,考虑一下变量(指针)的范围。正如@whozCraig所说,result =(在更正的realloc()中)为自动变量分配了一个值。传递给调用方的原始结果保持不变,现在悬而未决。这必须使用in / out arg或函数返回结果来处理。

因此,您可以做的就是通过更改此代码来简单地返回该指针:

void string_reallocation(char *result, int result_length) {

对此:

char* string_reallocation(char *result, int result_length) {
  // ...
  return result;
}

,然后将对此函数的调用更改为:

result = string_reallocation(result, current_length);

答案 1 :(得分:0)

您的代码中存在两个重大问题:

  • 保留realloc通话的结果。
  • 将结果返回给呼叫者。

此外,解决这些问题后,还有一个重要的体系结构问题。

  • 甚至还需要string_reallocation吗?

保留realloc

的结果

重新分配的结果将被忽略。 realloc是一个请求,要求获取一个(可能为NULL)先前动态分配的内存区域并对其进行扩展或收缩。结果 可能会导致隐藏的新分配丢失,realloc然后将旧的内存复制到新的内存中,解除旧的内存分配,最后返回指向新的内存的指针。您应该始终假定会发生这种情况。

因此,realloc的结果势在必行,必须保留。

result = realloc(result, result_length + 1);

注意:这不是最佳实践,因为realloc可能会失败,但是由于您是教师可疑的指导,所以我们假设分配成功。


realloc结果返回给呼叫者

由于realloc可能会导致出现一个新的内存区域,这意味着该指针的值可能会更改。但是,即使使用上面的修复程序来保留result,您实际上也没有将其返回给调用方。

result = ...

对传递给该函数的调用者指针无任何作用。因此,一旦将该区域替换为新区域,就会发生两次灾难:(1)调用方留下了悬空的指针,(2)引入了新分配区域的内存泄漏。

要解决此问题,我们需要将指针返回给调用者。最明显的解决方案是仅使用否则会无用的函数结果。更改函数声明以返回char *,如下所示:

// HERE: changed result type
char *string_reallocation(char *result, int result_length) 
{
    char *temp_result = malloc((strlen(result) + 1) * sizeof(char));

    strcpy(temp_result, result);

    result = realloc(result, (result_length + 1) * sizeof(char));

    strcpy(result, temp_result);

    free(temp_result);

    return result; // HERE: returned new region pointer
}

并更改呼叫者:

result = string_reallocation(result, current_length);

但是等等。还有更多。


甚至还需要string_reallocation

还记得我在这开始时所说的话吗? realloc将根据需要分配一个新的内存块,将旧块的大小内容复制到新的,然后释放旧块并返回生成的新块指针。

这意味着您的字符串复制操作(包括malloc的临时空间)是realloc已经为您完成的重复工作。换句话说,整个功能可以简化为:

 char *string_reallocation(char *result, int result_length) 
{
    result = realloc(result, result_length + 1);
    return result;
}

因此,使该功能完全毫无意义。您可以自己在呼叫者那边做那个,完全摆脱string_reallocation

while (current_node != NULL) {
    current_value_to_string = current_node->toStringFunc(current_node->value);
    current_value_length = (int) strlen(current_value_to_string);
    current_length += current_value_length + arrow_length;

    result = realloc(result, current_length + 1);

    strcat(result, current_value_to_string);
    strcat(result, arrow);

    current_node = current_node->next;
}

当然,我们看不到整个图片。我不知道result在此循环之前最初所指的是什么,这将留给您自己弄清楚。此外,我不禁要问toStringFunc操作实际上是做什么的(如果它是动态分配的,那是导致内存泄漏的原因)。无论如何,这应该可以解决您遇到的扩展问题。