Rou*_*oun 32 c memory memory-leaks memory-management
假设这是我的代码的一部分:
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);
}
Run Code Online (Sandbox Code Playgroud)
由于第一个可能malloc成功但第二个失败,我free(p)在第二个"错误处理程序"中使用.但是,如果有更多malloc的,如果我想修改代码(调整他们的订单,添加或删除一些代码malloc)怎么办?
我知道在C++中有RAII和异常安全等等.但总的来说,malloc在C中处理故障的正确方法是什么?(也许用一些goto?)
abl*_*igh 35
你的代码很好,但对于很多变量,我更喜欢:
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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,释放NULL被定义为无操作.
这避免n了n变量的缩进级别.您可以类似地清理文件句柄等(尽管您必须在周围设置条件close()).
现在,如果你知道你可以一次性分配它们,那么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;
}
Run Code Online (Sandbox Code Playgroud)
最终可能性:如果您确实想要在malloc失败时退出程序,请考虑使用mallopt's M_CHECK_ACTION选项.这会使malloc()故障得到检查和调用abort(),可能会打印出有用的消息.
从手册页:
名称
mallopt- 设置内存分配参数概要
Run Code Online (Sandbox Code Playgroud)#include <malloc.h> int mallopt(int param, int value);描述
该
mallopt()函数调整控制内存分配函数行为的参数(请参阅参考资料malloc(3)).的param参数指定要修改的参数,以及value指定该参数的新值.可以为以下值指定以下值
param:
M_CHECK_ACTION设置此参数可控制glibc在检测到各种编程错误时的响应方式(例如,将相同的指针释放两次).分配给此参数的值的3个最低有效位(2,1和0)确定glibc行为,如下所示:
位0:如果该位置1,则打印单行消息
stderr,提供有关错误的详细信息.消息以字符串开头"*** glibc detected ***",后跟程序名称,检测到错误的内存分配函数的名称,错误的简要描述以及检测到错误的内存地址.位1:如果该位置1,则在打印由位0指定的任何错误消息后,程序将通过调用终止
abort(3).在自2.4之后的glibc版本中,如果还设置了位0,则在打印错误消息和中止之间,程序还以方式打印堆栈跟踪backtrace(3),并以/proc/[pid]/maps(参见proc(5))的方式打印进程的内存映射.位2 :(因为glibc 2.4)只有当位0置位时,该位才有效.如果设置了该位,则简化描述错误的单行消息,以仅包含检测到错误的函数的名称以及错误的简要描述.
das*_*ght 25
既然是完全可以通过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);
Run Code Online (Sandbox Code Playgroud)
只要没有中间依赖关系,即没有具有多级依赖关系的结构,这就可以工作.当你这样做时,最好定义一个释放这种结构的函数,而不要假设所有的内存块都是非结构的NULL.
对于大量分配,我会投入时间创建一个内存管理器来跟踪分配。这样,无论函数是否成功,您都不必担心泄漏。
总体思路是创建一个包装器来malloc记录成功的分配,然后根据请求释放它们。要释放内存,您只需将特殊大小传递给包装函数即可。0如果您知道实际的分配都不会用于指定大小的块,则使用大小来释放内存是合适的0。否则,您可能希望用作~0ULL请求释放大小。
下面是一个简单的示例,允许在两次释放之间最多进行 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;
}
Run Code Online (Sandbox Code Playgroud)