释放从 realloc 返回的空指针?

Stu*_*ent 6 c memory realloc

当我有一个应该重复用作参数realloc并保存其返回值的指针时,我知道realloc如果无法进行分配,则不会触及旧对象,返回NULL. 我是否还应该担心以下结构中的旧对象:

int *p = (int *)malloc(sizeof(int *));
if (p = (int *)realloc(p, 2 * sizeof(int *)))
 etc...
Run Code Online (Sandbox Code Playgroud)

现在,如果realloc成功了,我需要free(p)在完成后进行。当realloc失败时,我已指定它返回NULL并且pfree(p)执行任何操作(因为它是 a free(NULL))。同时(根据标准)旧对象不会被释放。p那么我是否应该有一个(例如)的副本int *last_p = p;来跟踪旧对象,以便free(last_p)realloc失败时可以?

Ger*_*rdh 14

那么我是否应该拥有 p 的副本(例如 int *last_p = p;)来跟踪旧对象,以便在 realloc 失败时可以释放(last_p)?

基本上:是的,当然。

通常不建议使用您显示的模式:

p = (int *)realloc(p, ...) 
Run Code Online (Sandbox Code Playgroud)

由于您发现的原因,这被认为是不好的做法。

使用这个代替:

void *new = realloc(p, ...);
if (new != NULL)
  p = new;
else
  ...
Run Code Online (Sandbox Code Playgroud)

在 C 中强制转换 等的返回值也被认为是不好的做法mallocrealloc这不是必需的。

  • @Student完全取决于您的项目要求和流程。也许您想携带已经分配的数据(“p”),并且知道(目前)没有更多内存可用。也许您想释放一些数据。也许您想出错并停止执行。也许还有别的东西......? (3认同)
  • 另外,不要说“免费`p`”。您没有分配或释放指针;您正在分配或释放内存。你不应该将“malloc”、“realloc”和“free”视为分配或释放指针,即指针类型的对象。指针类型的对象仅保存指针值。`malloc` 和 `realloc` 返回一个指针值,将其存储在哪个或哪些对象中与其操作或 `free` 操作无关。您使用什么对象来获取传递给“free”的值是无关紧要的;仅使用传递的指针值。 (2认同)

klu*_*utt 5

你应该保存一个临时指针吗?

正如之前的答案所指出的,在某些情况下这可能是一件好事。前提是你有一个失败后如何继续执行的计划。

最重要的不是你如何处理错误。重要的是你要做一些事情,而不仅仅是假设没有错误。退出是处理错误的一种完全有效的方法。

不要这样做,除非你计划合理的恢复

但是,请注意,在大多数情况下,故障realloc很难恢复。退出往往是唯一明智的选择。如果你无法获得足够的内存来完成你的任务,你会怎么做?我曾经遇到过一种情况,恢复只有一次是明智的。我有一个解决问题的算法,我意识到如果分配几 GB 的内存,我可以显着提高性能。它在只有几千字节的情况下工作得很好,但随着额外的内存使用,它的速度明显加快。所以代码基本上是这样的:

int *huge_buffer = malloc(1000*1000*1000*sizeof *hugebuffer);
if(!huge_buffer) 
    slow_version();
else
    fast_version();
Run Code Online (Sandbox Code Playgroud)

在这些情况下,只需执行以下操作:

p = realloc(p, 2 * sizeof *p)
if(!p) {
    fprintf(stderr, "Error allocating memory");
    exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)

请注意通话的两项更改。我删除了铸造并更改了尺寸。在这里阅读更多相关内容:Do I Cast the result of malloc?

或者甚至更好,如果你一般不关心的话。写一个包装器。

void *my_realloc(void *p, size_t size) {
    void *tmp = realloc(p, size);

    if(tmp) return tmp;

    fprintf(stderr, "Error reallocating\n");
    free(p);
    exit(EXIT_FAILURE);
    
    return NULL; // Will never be executed, but to avoid warnings
}
Run Code Online (Sandbox Code Playgroud)

请注意,这可能与我在下面写的内容相矛盾,我在下面写的是退出之前并不总是需要释放。原因是,当我将其全部抽象为单个函数时,正确的错误处理非常容易做到,所以我最好做得正确。在这种情况下只需要额外的一行。对于整个程序来说。

相关:当你在 malloc 之后不释放时,到底会发生什么?

关于一般的向后兼容性

有些人会说,在退出之前释放是一个很好的做法,因为它在某些情况下确实很重要。我的观点是,这些情况是非常特殊的,例如当使用操作系统编写嵌入式系统时,操作系统在终止时不会自动释放内存。如果您在这样的环境中编码,您应该知道这一点。如果您正在为一个可以为您完成此操作的操作系统进行编码,那么为什么不利用它来降低代码复杂性呢?

总的来说,我认为一些 C 程序员过于关注与 1970 年代的古代计算机的向后兼容性,而这些计算机今天只能在博物馆中遇到。在大多数情况下,假设 ASCII、二进制补码、8 位字符大小等都是相当公平的。

一个比较是仍然对网页进行编码,以便可以在 Netscape Navigator、Mosaic 和 Lynx 中查看它们。仅当确实有需要时才花时间。

即使你跳过向后兼容性,也要使用一些防护措施

但是,每当您做出假设时,包含一些元代码可能是一件好事,这些代码会导致编译因错误目标而失败。例如,如果您的程序依赖于 8 位字符:

_Static_assert(CHAR_BITS == 8, "Char bits");
Run Code Online (Sandbox Code Playgroud)

这将使你的程序在编译时崩溃。如果您正在进行交叉编译,这可能会更复杂。我不知道该怎么做。

  • @学生 是的。大多数情况下,退出前不必释放。除非您正在为非常特定的操作系统编写代码,不包括 Win、Linux 和 Mac。 (2认同)