[英]How can I correctly handle malloc failure in C, especially when there is more than one malloc?
Suppose this is a part of my code: 假设这是我的代码的一部分:
int foo()
{
char *p, *q ;
if((p = malloc(BUFSIZ)) == NULL) {
return ERROR_CODE;
}
if((q = malloc(BUFSIZ)) == NULL) {
free(p)
return ERROR_CODE;
}
/* Do some other work... */
free(p);
free(q);
}
Since it's possible that the first malloc
is successful but the second one fails, I use free(p)
in the second "error handler". 由于第一个
malloc
可能成功而第二个malloc
失败,所以我在第二个“错误处理程序”中使用free(p)
。 But what if there are more malloc
's and what if I want to modify the code (adjusting their orders, adding or deleting some malloc
)? 但是,如果还有更多的
malloc
呢?如果我想修改代码(调整它们的顺序,添加或删除一些malloc
)怎么办?
I know in C++ there are things like RAII and exception safe, etc. But in general, what is the correct way to handle malloc
failure in C? 我知道在C ++中有诸如RAII和异常安全之类的东西。但是总的来说,在C中处理
malloc
失败的正确方法是什么? (maybe using some goto
?) (也许使用一些
goto
吗?)
Your code is fine, but for lots of variables, I'd prefer: 您的代码很好,但是对于许多变量,我希望:
int
foo()
{
char *p = NULL;
char *q = NULL;
int ret = 0;
if (NULL == (p = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
goto error;
}
// possibly do something here
if (NULL == (q = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
goto error;
}
// insert similar repetitions
// hopefully do something here
error:
free (p);
free (q);
return ret;
}
Note that freeing NULL
is defined as a no-op. 请注意,将
NULL
释放定义为无操作。
This avoids n
levels of indent for n
variables. 这避免了
n
n
变量的n
个缩进级别。 You can clean up filehandles etc. similarly (though you'll have to put a condition around the close()
). 您可以类似地清理文件句柄等(尽管您必须在
close()
周围放置一个条件)。
Now, if you know you can allocate them all at once, then dasblinkenlight has a good answer, but here's another way: 现在,如果您知道可以一次分配它们,那么dasblinkenlight有一个很好的答案,但是这是另一种方法:
int
foo()
{
int ret = 0;
char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (!p || !q || !r)
{
ret = ERROR_CODE;
goto exit;
}
// do something
exit:
free(p);
free(q);
free(r);
return ret;
}
Final possibility: if you actually want to exit the program on malloc
fail, consider using mallopt
's M_CHECK_ACTION
option. 最终可能性:如果您确实想在
malloc
失败时退出程序,请考虑使用mallopt
的M_CHECK_ACTION
选项。 This makes malloc()
faults get checked, and calls abort()
, possibly printing a helpful message. 这使得
malloc()
错误得到检查,并调用abort()
,可能会显示一条有用的消息。
From the man page: 从手册页:
NAME
名称
mallopt
- set memory allocation parametersmallopt
设置内存分配参数SYNOPSIS
概要
#include <malloc.h> int mallopt(int param, int value);
DESCRIPTION
描述
The
mallopt()
function adjusts parameters that control the behavior of the memory-allocation functions (seemalloc(3)
).mallopt()
函数调整用于控制内存分配函数行为的参数(请参阅malloc(3)
)。 Theparam
argument specifies the parameter to be modified, andvalue
specifies the new value for that parameter.param
参数指定要修改的参数,而value
指定该参数的新值。The following values can be specified for
param
:可以为
param
指定以下值:
M_CHECK_ACTION
Setting this parameter controls how glibc responds when various kinds of programming errors are detected (eg, freeing the same pointer twice).
设置此参数可控制在检测到各种编程错误(例如,释放同一指针两次)时glibc的响应方式。 The 3 least significant bits (2, 1, and 0) of the value assigned to this parameter determine the glibc behavior, as follows:
分配给此参数的值的3个最低有效位(2、1,和0)确定glibc行为,如下所示:
Bit 0 : If this bit is set, then print a one-line message on
stderr
that provides details about the error.位0 :如果该位置1,则在
stderr
上打印一条单行消息,其中提供有关错误的详细信息。 The message starts with the string"*** glibc detected ***"
, followed by the program name, the name of the memory-allocation function in which the error was detected, a brief description of the error, and the memory address where the error was detected.该消息以字符串
"*** glibc detected ***"
开头,然后是程序名称,在其中检测到错误的内存分配函数的名称,错误的简要说明以及内存地址,其中检测到错误。Bit 1 : If this bit is set, then, after printing any error message specified by bit 0, the program is terminated by calling
abort(3)
.位1 :如果设置了此位,则在打印由位0指定的任何错误消息之后,通过调用
abort(3)
终止程序。 In glibc versions since 2.4, if bit 0 is also set, then, between printing the error message and aborting, the program also prints a stack trace in the manner ofbacktrace(3)
, and prints the process's memory mapping in the style of/proc/[pid]/maps
(seeproc(5)
).在glibc的版本,因为2.4,如果位0也被置位,则,打印错误消息并中止之间,该程序还打印在上的状态的堆栈跟踪
backtrace(3)
并打印在风格进程的存储器映射/proc/[pid]/maps
(请参阅proc(5)
)。Bit 2 : (since glibc 2.4) This bit has an effect only if bit 0 is also set.
位2 :(自glibc 2.4起)仅当还设置了位0时,此位才有效。 If this bit is set, then the one-line message describing the error is simplified to contain just the name of the function where the error was detected and the brief description of the error.
如果设置了此位,则描述错误的单行消息将简化为仅包含检测到错误的函数的名称以及错误的简短描述。
Since it is perfectly OK to pass NULL
to free()
, you could allocate everything that you need in a "straight line", check everything in a single shot, and then free everything once you are done, regardless of whether or not you have actually done any work: 由于将
NULL
传递给free()
是完全可以的,因此您可以在“直线”中分配所需的所有内容,一次性检查所有内容,然后在完成后释放所有内容,无论您是否拥有实际完成了任何工作:
char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (p && q && r) {
/* Do some other work... */
}
free(p);
free(q);
free(r);
This works as long as there are no intermediate dependencies, ie you do not have structures with multi-level dependencies. 只要没有中间依赖项,即不具有多级依赖项的结构,此方法就起作用。 When you do, it is a good idea to define a function for freeing such a structure, without assuming that all memory blocks are non-
NULL
. 在执行此操作时,最好定义一个用于释放这种结构的函数,而不假定所有内存块均为非
NULL
。
For large numbers of allocations, I would invest the time in creating a memory manager that keeps track of the allocations. 对于大量分配,我会花时间创建一个跟踪分配的内存管理器。 That way, you never have to worry about leaks, regardless of whether or not the function succeeds.
这样,无论功能是否成功,您都不必担心泄漏。
The general idea is to create a wrapper for malloc
that records successful allocations, and then frees them on request. 通常的想法是为
malloc
创建一个包装器,以记录成功的分配,然后根据请求释放它们。 To free memory, you simply pass a special size to the wrapper function. 要释放内存,只需将特殊大小传递给包装函数。 Using a size of
0
to free memory is appropriate if you know that none of your actual allocations will be for 0
sized blocks. 使用尺寸的
0
来释放内存,如果你知道你没有实际分配的将是适合0
大小的块。 Otherwise, you may want to use ~0ULL
as the request-to-free size. 否则,您可能希望使用
~0ULL
作为免费请求大小。
Here's a simple example that allows up to 100 allocations between frees. 这是一个简单的示例,每次释放之间最多允许100个分配。
#define FREE_ALL_MEM 0
void *getmem( size_t size )
{
static void *blocks[100];
static int count = 0;
// special size is a request to free all memory blocks
if ( size == FREE_ALL_MEM )
{
for ( int i = 0; i < count; i++ )
free( blocks[i] );
count = 0;
return NULL;
}
// using a linked list of blocks would allow an unlimited number of blocks
// or we could use an array that can be expanded with 'realloc'
// but for this example, we have a fixed size array
if ( count == 100 )
return NULL;
// allocate some memory, and save the pointer in the array
void *result = malloc( size );
if ( result )
blocks[count++] = result;
return result;
}
int foo( void )
{
char *p, *q;
if ( (p = getmem(BUFSIZ)) == NULL ) {
return ERROR_CODE;
}
if ( (q = getmem(BUFSIZ)) == NULL ) {
getmem( FREE_ALL_MEM );
return ERROR_CODE;
}
/* Do some other work... */
getmem( FREE_ALL_MEM );
return SUCCESS_CODE;
}
it is matter of habit, but I prefer: 这是习惯问题,但我更喜欢:
int returnFlag = FAILURE;
if ((p = malloc...) != NULL)
{
if ((q = malloc..) != NULL)
{
// do some work
returnFlag = SUCCESS; // success only if it is actually success
free(q);
}
free(p);
}
return returnFlag; // all other variants are failure
IF you are expecting to allocate a large number of items, it Can get messy. 如果您希望分配大量的项目,它可能会变得凌乱。 Try to avoid the 'goto' approach.
尝试避免“ goto”方法。 Not because of the old 'goto is bad' ethic, but because that way really can lie madness and memory leaks.
并不是因为过去的“ goto is bad”道德准则,而是因为这种方式确实可以掩盖疯狂和内存泄漏。
It's a little overkill for small numbers of malloc, but you can consider something like this approach: 对于少量的malloc来说,这有点矫kill过正,但是您可以考虑使用以下方法:
void free_mem(void **ptrs, size_t len)
{
for (size_t i = 0; i < len; ++i)
{
free(ptrs[i]);
ptrs[i] = NULL;
}
}
int foo(...)
{
void *to_be_freed[N];
int next_ptr = 0;
for (size_t i = 0; i < N; ++i) to_be_freed[i] = NULL;
p = malloc(..);
if (!p)
{
free_mem(to_be_freed,N);
return ERROR_CODE;
}
to_be_freed[next_ptr++] = p;
// Wash, rinse, repeat, with other mallocs
free_mem(to_be_freed,N)
return SUCCESS;
}
In reality, you can probably wrap malloc with something which tracks this. 实际上,您可以使用跟踪此内容的东西包装malloc。 Put the array and array size in a structure and pass that in with the desired allocation size.
将数组和数组大小放入结构中,然后将其传递给所需的分配大小。
I think the first answer is the most general purpose as it can be used for errors other than those caused by malloc. 我认为第一个答案是最通用的,因为它可以用于除malloc引起的错误以外的其他错误。 However I would remove the gotos and use a single pass while loop like so.
但是我会删除gotos并像这样使用一次单次while循环。
int foo()
{
char *p = NULL;
char *q = NULL;
int ret = 0;
do {
if (NULL == (p = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
break;
}
// possibly do something here
if (NULL == (q = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
break;
}
// insert similar repetitions
// hopefully do something here
} while(0);
free (p);
free (q);
return ret;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.