释放从 realloc 返回的空指针?
Freeing a null pointer returned from realloc?
当我有一个指针应该重复用作 realloc
的参数并保存它的 return 值时,我知道 realloc
不会触及旧对象,如果无法进行分配,returning NULL
。我是否还应该担心像这样的结构中的旧对象:
int *p = (int *)malloc(sizeof(int *));
if (p = (int *)realloc(p, 2 * sizeof(int *)))
etc...
现在,如果 realloc
成功,我需要在完成后 free(p)
。
当 realloc
失败时,我已将其 return NULL
分配给 p
并且 free(p)
不执行任何操作(因为它是 free(NULL)
) .同时(根据标准)不释放旧对象。
那么我是否应该有一个 p
的副本(例如 int *last_p = p;
)来跟踪旧对象,以便在 realloc
失败时我可以 free(last_p)
?
So should I have a copy of p (e.g. int *last_p = p;) for keeping track of the old object, so that I can free(last_p) when realloc fails?
是的,没错。通常我看到新指针被分配:
void *pnew = realloc(p, 2 * sizeof(int *)))
if (pnew == NULL) {
free(p);
/* handle error */
return ... ;
}
p = pnew;
So should I have a copy of p (e.g. int *last_p = p;) for keeping track of the old object, so that I can free(last_p) when realloc fails?
基本上:是的,当然。
一般不建议使用您展示的模式:
p = (int *)realloc(p, ...)
由于您发现的原因,这被认为是不好的做法。
改用这个:
void *new = realloc(p, ...);
if (new != NULL)
p = new;
else
...
在 C 中转换 malloc
、realloc
等的 return 值也被认为是不好的做法。这不是必需的。
你应该保存一个临时指针吗?
在某些情况下这可能是一件好事,正如之前的回答中所指出的那样。前提是你有失败后如何继续执行的计划。
最重要的不是你如何处理错误。重要的是你做了一些事情,而不是假设没有错误。退出是处理错误的一种完全有效的方式。
不要这样做,除非你计划一个明智的恢复
但是,请注意,在大多数情况下,realloc
导致的故障很难恢复。退出往往是唯一明智的选择。如果你不能为你的任务获得足够的记忆,你打算做什么?我遇到过只有一次恢复是明智的情况。我有一个解决问题的算法,我意识到如果我分配几千兆字节的 ram,我可以显着提高性能。它在只有几千字节的情况下运行良好,但随着额外的内存使用,它变得明显更快。所以代码基本上是这样的:
int *huge_buffer = malloc(1000*1000*1000*sizeof *hugebuffer);
if(!huge_buffer)
slow_version();
else
fast_version();
在那些情况下,只需这样做:
p = realloc(p, 2 * sizeof *p)
if(!p) {
fprintf(stderr, "Error allocating memory");
exit(EXIT_FAILURE);
}
请注意通话的两个变化。我删除了转换并更改了 sizeof。在这里阅读更多相关信息: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
}
请注意,这可能与我在下面写的内容相矛盾,我在下面写的是在退出之前并不总是需要释放。原因是,当我将所有错误都抽象为一个函数时,正确的错误处理很容易做到,所以我不妨把它做对。在这种情况下,它只需要额外的一行。对于整个程序。
相关:What REALLY happens when you don't free after malloc?
关于一般的向后兼容性
有人会说在退出前先释放是个好习惯,只是因为它在某些情况下确实很重要。我的观点是,这些情况非常特殊,例如在使用 OS 编写嵌入式系统时,它们在终止时不会自动释放内存。如果您在这样的环境中编码,您应该知道这一点。如果您正在为一个为您执行此操作的 OS 编写代码,那么为什么不利用它来降低代码复杂性呢?
总的来说,我认为一些 C 编码人员过分关注与 1970 年代的古老计算机的向后兼容性,而这些计算机今天只能在博物馆中遇到。在大多数情况下,假设 ascii、两个补码、8 位的字符大小等是相当公平的。
比较是对网页进行编码,以便可以在 Netscape Navigator、Mosaic 和 Lynx 中查看它们。只在确实有需要时才花时间在这上面。
即使你跳过向后兼容性,也要使用一些保护措施
但是,每当您做出假设时,包含一些元代码可能是一件好事,这些元代码会使编译因错误的目标而失败。例如,如果您的程序依赖于 8 位字符:
_Static_assert(CHAR_BITS == 8, "Char bits");
这将使您的程序在编译时崩溃。如果您正在进行交叉编译,这可能会更复杂。那我就不知道怎么办了。
当我有一个指针应该重复用作 realloc
的参数并保存它的 return 值时,我知道 realloc
不会触及旧对象,如果无法进行分配,returning NULL
。我是否还应该担心像这样的结构中的旧对象:
int *p = (int *)malloc(sizeof(int *));
if (p = (int *)realloc(p, 2 * sizeof(int *)))
etc...
现在,如果 realloc
成功,我需要在完成后 free(p)
。
当 realloc
失败时,我已将其 return NULL
分配给 p
并且 free(p)
不执行任何操作(因为它是 free(NULL)
) .同时(根据标准)不释放旧对象。
那么我是否应该有一个 p
的副本(例如 int *last_p = p;
)来跟踪旧对象,以便在 realloc
失败时我可以 free(last_p)
?
So should I have a copy of p (e.g. int *last_p = p;) for keeping track of the old object, so that I can free(last_p) when realloc fails?
是的,没错。通常我看到新指针被分配:
void *pnew = realloc(p, 2 * sizeof(int *)))
if (pnew == NULL) {
free(p);
/* handle error */
return ... ;
}
p = pnew;
So should I have a copy of p (e.g. int *last_p = p;) for keeping track of the old object, so that I can free(last_p) when realloc fails?
基本上:是的,当然。
一般不建议使用您展示的模式:
p = (int *)realloc(p, ...)
由于您发现的原因,这被认为是不好的做法。
改用这个:
void *new = realloc(p, ...);
if (new != NULL)
p = new;
else
...
在 C 中转换 malloc
、realloc
等的 return 值也被认为是不好的做法。这不是必需的。
你应该保存一个临时指针吗?
在某些情况下这可能是一件好事,正如之前的回答中所指出的那样。前提是你有失败后如何继续执行的计划。
最重要的不是你如何处理错误。重要的是你做了一些事情,而不是假设没有错误。退出是处理错误的一种完全有效的方式。
不要这样做,除非你计划一个明智的恢复
但是,请注意,在大多数情况下,realloc
导致的故障很难恢复。退出往往是唯一明智的选择。如果你不能为你的任务获得足够的记忆,你打算做什么?我遇到过只有一次恢复是明智的情况。我有一个解决问题的算法,我意识到如果我分配几千兆字节的 ram,我可以显着提高性能。它在只有几千字节的情况下运行良好,但随着额外的内存使用,它变得明显更快。所以代码基本上是这样的:
int *huge_buffer = malloc(1000*1000*1000*sizeof *hugebuffer);
if(!huge_buffer)
slow_version();
else
fast_version();
在那些情况下,只需这样做:
p = realloc(p, 2 * sizeof *p)
if(!p) {
fprintf(stderr, "Error allocating memory");
exit(EXIT_FAILURE);
}
请注意通话的两个变化。我删除了转换并更改了 sizeof。在这里阅读更多相关信息: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
}
请注意,这可能与我在下面写的内容相矛盾,我在下面写的是在退出之前并不总是需要释放。原因是,当我将所有错误都抽象为一个函数时,正确的错误处理很容易做到,所以我不妨把它做对。在这种情况下,它只需要额外的一行。对于整个程序。
相关:What REALLY happens when you don't free after malloc?
关于一般的向后兼容性
有人会说在退出前先释放是个好习惯,只是因为它在某些情况下确实很重要。我的观点是,这些情况非常特殊,例如在使用 OS 编写嵌入式系统时,它们在终止时不会自动释放内存。如果您在这样的环境中编码,您应该知道这一点。如果您正在为一个为您执行此操作的 OS 编写代码,那么为什么不利用它来降低代码复杂性呢?
总的来说,我认为一些 C 编码人员过分关注与 1970 年代的古老计算机的向后兼容性,而这些计算机今天只能在博物馆中遇到。在大多数情况下,假设 ascii、两个补码、8 位的字符大小等是相当公平的。
比较是对网页进行编码,以便可以在 Netscape Navigator、Mosaic 和 Lynx 中查看它们。只在确实有需要时才花时间在这上面。
即使你跳过向后兼容性,也要使用一些保护措施
但是,每当您做出假设时,包含一些元代码可能是一件好事,这些元代码会使编译因错误的目标而失败。例如,如果您的程序依赖于 8 位字符:
_Static_assert(CHAR_BITS == 8, "Char bits");
这将使您的程序在编译时崩溃。如果您正在进行交叉编译,这可能会更复杂。那我就不知道怎么办了。