【问题标题】:If Realloc() fails, does the caller lose the data prom the previous malloc() call?如果 Realloc() 失败,调用者会丢失前一次 malloc() 调用的数据吗?
【发布时间】:2020-09-10 00:53:29
【问题描述】:

以下是考试引述(1% 顶尖大学)。

我失败了,因为我的答案与“批准”的答案不同。

我有一种预感,他(教授,著名的 C 语言专家)的答案不正确。

以下是“已批准”答案后跟的问题。

以下函数存在潜在错误。它是什么,我该如何解决?

提示:这与 realloc() 函数的使用有关。请确定您要更改的行号以及替换它们的内容。

BOOLEAN lengthen_string(char* string, const char newcontents[])
{
        int newlen = strlen(string) + strlen(newcontents) + 1;
        string = realloc(string, newlen);

        if (!string) {
                perror("malloc");
                return FALSE;
        }

        strcat(string, newcontents);
        return TRUE;
}

教授提供的“正确”答案是:

第 4 行:realloc 在分配失败时返回一个 NULL 指针。这意味着一旦失败,原始数据就会丢失。

要解决此问题,请将 realloc 的结果分配给一个临时变量并首先对其进行测试。

即:第 4 行:

char * temp=realloc(string, newlen);
if(!temp) ... (all remains the same)

在旧第 9 行之后,string = temp

有什么想法吗?

顺便说一句,我的回答是 @string 是一个局部变量,函数的原型应该是 char **string,调用者将指针传递给它的字符串指针,然后被调用者分配任何 realloc()返回值到*string

有什么想法吗?

【问题讨论】:

  • 你为什么要删除原来的问题?你对那个测试问题的回答是绝对正确的。在不更新原始指针的情况下重新分配字符串是一个主要缺陷。它确实与realloc() 有关,因为realloc() 很棘手且容易出错,但是任何其他方法,malloc 都会有同样的问题,除了更明显。报告这个问题并纠正你的分数同样棘手,教授通常不喜欢矛盾。私下做,谦虚,每个人都会犯错,每天学习新东西是我的座右铭。
  • 请不要破坏您的帖子。通过在 Stack Exchange 网络上发布,您已授予 SE 分发该内容的不可撤销的权利(在 CC-BY-SA 4.0 license 下)。根据 SE 政策,任何破坏行为都将被撤销,随后的尝试将使您被禁止。如果您想取消此帖子与您帐户的关联,请参阅What is the proper route for a disassociation request?

标签: c memory malloc glibc realloc


【解决方案1】:

你们都是对的。

教授是正确的,因为realloc() 在失败时不会改变传入的内存,因此输入string 指针完好无损,但如果失败时的NULL 返回值立即分配给string 然后原始数据丢失和泄露。因此,在将新指针值分配给string 之前,需要先检查失败。

您是对的,因为string 需要通过指针传递,因此如果realloc() 返回不同的内存地址,它可以重新分配一个新值。

正确的解决方案应该是这样的:

BOOLEAN lengthen_string(char** string, const char newcontents[])
{
    if (!string)
    {
        errno = EINVAL;
        perror("bad input");
        return FALSE;
    }
    size_t newsize = strlen(*string) + strlen(newcontents) + 1;
    char *temp = realloc(*string, newsize);
    if (!temp)
    {
        perror("realloc failed");
        return FALSE;
    }
    strcat(temp, newcontents);
    *string = temp;
    return TRUE;
}

或者,还有一些优化空间,例如:

BOOLEAN lengthen_string(char** string, const char newcontents[])
{
    if (!string)
    {
        errno = EINVAL;
        perror("bad input");
        return FALSE;
    }
    char *temp;
    if (!*string)
    {
        temp = strdup(newcontents);
        if (!temp)
        {
            perror("strdup failed");
            return FALSE;
        }
    }
    else
    {
        size_t offset = strlen(*string);
        size_t size = strlen(newcontents) + 1;
        temp = realloc(*string, offset + size);
        if (!temp)
        {
            perror("realloc failed");
            return FALSE;
        }
        memcpy(temp + offset, newcontents, size);
    }
    *string = temp;
    return TRUE;
}

【讨论】:

  • 次要:EINVAL 不是标准 C 库的一部分。也许它总是在 glibc 中可用?
  • @chux-ReinstateMonica,C 标准只需要 EDOMEILSEQERANGE,但在大多数 POSIX/'Nix 实现中都可以使用 EINVAL 等其他标准。
猜你喜欢
  • 1970-01-01
  • 2010-12-09
  • 2021-04-02
  • 2012-12-14
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 2010-10-15
  • 2022-10-01
相关资源
最近更新 更多