C 中的清除函数
Cleanup function in C
我有一个 C 程序,它分配了一些缓冲区,需要在程序退出执行之前清除这些缓冲区。我的 main
程序看起来像这样
int main(int argc, char** argv){
// Some code
// ...
// ...
void* data1 = malloc(someSize);
void* data2 = malloc(someSize);
double* result = malloc(resultSize);
double* buffer = malloc(buffSize);
// Some code
// ...
// First exit point
if(someExitcondition){
// free all allocated memory
exit(1);
}
// Some code
// ...
// Second exit point
if(someOtherExitcondition){
// free all allocated memory
exit(1);
}
// Some code
// ...
// free all allocated memory
return 0;
}
我想通过调用 cleanUp()
函数来简化清理工作,该函数将 free
在堆中分配的所有内存。我想在每个 exit(1)
调用之前和 return 0
行之前调用此函数(基本上用对 cleanUp()
的调用替换每个评论 // free all allocated memory
)。 我的问题是如何将指针 data1
、data2
、result
和 buffer
传递给 cleanUp()
以便它们可以被释放?
这就是我想做的。这是正确的方法吗?
void cleanUp(void* p1, void* p2, void* p3, void* p4){
// call to free for each pointer
}
或多或少,但也许更好的方法是使用 atexit()
注册回调。只要程序正常退出(通过 exit()
,或在 return 从 main()
到启动代码),就会调用它。缺点是清理函数的签名是 void cleanup(void)
— 没有参数。这意味着该函数必须能够访问全局(或文件范围)变量。但它为执行清理提供了最大的可靠性。
还有一种说法是,如果你的程序即将退出,你是否调用free()
并不重要——系统无论如何都会释放内存。但是,如果您不释放内存,像 Valgrind 这样的程序会抱怨丢失内存 — 即使如此,释放它通常也是一种很好的做法。
如果您曾经被教导不要使用 goto
,那么类似的东西就是使用 goto
.
的好时机的典型例子
int main(int argc, char** argv){
int ret_status = 0;
// Some code
// ...
// ...
void* data1 = malloc(someSize);
void* data2 = malloc(someSize);
double* result = malloc(resultSize);
double* buffer = malloc(buffSize);
// allocation failure: first exit point
if(!(data1 && data2 && result && buffer)){
ret_status = 1;
goto cleanup;
}
// Some code
// ...
// Second exit point
if(someOtherExitcondition){
ret_status = 1;
goto cleanup;
}
// Some code
// ...
cleanup:
// free all allocated memory
free(data1);
free(data2);
free(result);
free(buffer);
return ret_status;
}
如果与给定的示例不同,您还从嵌套函数调用 exit()
,请查看 Jonathan Leffler 的回答中给出的使用 atexit()
。
只需调用
cleanUp(data1, data2, result, buffer);
因为这些变量的值是已分配的指针,这就是您无论如何要传递给 free()
的内容。
我认为更好的方法是在函数末尾的清理部分使用 goto
语句。这是 goto
利大于弊的少数情况之一。想象一下,如果您有 50 个使用这种缓冲区的函数,并且它们都应该有自己相应的清理函数,您的代码会是什么样子。
查看 Christian Gibbons 的回答。
您应该考虑使用非标准 cleanup
功能,该功能由gcc
和clang
支持,它简化了代码,并使您的代码更安全。
void free_void(void **value) { free(*value); }
void free_double(double **value) { free(*value); }
int main()
{
/* Return any time you like and allocated things get freed */
__attribute__((cleanup(free_void))) void *data1 = malloc(someSize);
__attribute__((cleanup(free_void))) void *data2 = malloc(someSize);
__attribute__((cleanup(free_double))) double *result = malloc(resultSize);
__attribute__((cleanup(free_double))) double *buffer = malloc(buffSize);
return 0;
}
你可以用宏来简化这个:
static inline void free_void(void **value) { free(*value); }
#define cleanup__void_ptr __attribute__((cleanup(free_void))) void *
static inline void free_double(double **value) { free(*value); }
#define cleanup__double_ptr __attribute__((cleanup(free_double))) double *
int main()
{
/* Return any time you like and allocated things get freed */
cleanup__void_ptr data1 = malloc(someSize);
cleanup__void_ptr data2 = malloc(someSize);
if(!data2) {
return 0;
/* calls `free()` on data1 and data2. */
}
cleanup__double_ptr result = malloc(resultSize);
cleanup__double_ptr buffer = malloc(buffSize);
return 0;
/* calls `free()` on data1, data2, result and buffer */
}
我强烈建议不要使用 setjmp()
/longjmp()
,而是将清理函数附加到任何已分配的资源,这样您就可以在任何地方使用 return
语句。函数的任何调用者都应该检查 return 值。如果错误提示return立即处理,或者处理异常。这样你就不需要 setjmp()
/longjmp()
.
我有一个 C 程序,它分配了一些缓冲区,需要在程序退出执行之前清除这些缓冲区。我的 main
程序看起来像这样
int main(int argc, char** argv){
// Some code
// ...
// ...
void* data1 = malloc(someSize);
void* data2 = malloc(someSize);
double* result = malloc(resultSize);
double* buffer = malloc(buffSize);
// Some code
// ...
// First exit point
if(someExitcondition){
// free all allocated memory
exit(1);
}
// Some code
// ...
// Second exit point
if(someOtherExitcondition){
// free all allocated memory
exit(1);
}
// Some code
// ...
// free all allocated memory
return 0;
}
我想通过调用 cleanUp()
函数来简化清理工作,该函数将 free
在堆中分配的所有内存。我想在每个 exit(1)
调用之前和 return 0
行之前调用此函数(基本上用对 cleanUp()
的调用替换每个评论 // free all allocated memory
)。 我的问题是如何将指针 data1
、data2
、result
和 buffer
传递给 cleanUp()
以便它们可以被释放?
这就是我想做的。这是正确的方法吗?
void cleanUp(void* p1, void* p2, void* p3, void* p4){
// call to free for each pointer
}
或多或少,但也许更好的方法是使用 atexit()
注册回调。只要程序正常退出(通过 exit()
,或在 return 从 main()
到启动代码),就会调用它。缺点是清理函数的签名是 void cleanup(void)
— 没有参数。这意味着该函数必须能够访问全局(或文件范围)变量。但它为执行清理提供了最大的可靠性。
还有一种说法是,如果你的程序即将退出,你是否调用free()
并不重要——系统无论如何都会释放内存。但是,如果您不释放内存,像 Valgrind 这样的程序会抱怨丢失内存 — 即使如此,释放它通常也是一种很好的做法。
如果您曾经被教导不要使用 goto
,那么类似的东西就是使用 goto
.
int main(int argc, char** argv){
int ret_status = 0;
// Some code
// ...
// ...
void* data1 = malloc(someSize);
void* data2 = malloc(someSize);
double* result = malloc(resultSize);
double* buffer = malloc(buffSize);
// allocation failure: first exit point
if(!(data1 && data2 && result && buffer)){
ret_status = 1;
goto cleanup;
}
// Some code
// ...
// Second exit point
if(someOtherExitcondition){
ret_status = 1;
goto cleanup;
}
// Some code
// ...
cleanup:
// free all allocated memory
free(data1);
free(data2);
free(result);
free(buffer);
return ret_status;
}
如果与给定的示例不同,您还从嵌套函数调用 exit()
,请查看 Jonathan Leffler 的回答中给出的使用 atexit()
。
只需调用
cleanUp(data1, data2, result, buffer);
因为这些变量的值是已分配的指针,这就是您无论如何要传递给 free()
的内容。
我认为更好的方法是在函数末尾的清理部分使用 goto
语句。这是 goto
利大于弊的少数情况之一。想象一下,如果您有 50 个使用这种缓冲区的函数,并且它们都应该有自己相应的清理函数,您的代码会是什么样子。
查看 Christian Gibbons 的回答。
您应该考虑使用非标准 cleanup
功能,该功能由gcc
和clang
支持,它简化了代码,并使您的代码更安全。
void free_void(void **value) { free(*value); }
void free_double(double **value) { free(*value); }
int main()
{
/* Return any time you like and allocated things get freed */
__attribute__((cleanup(free_void))) void *data1 = malloc(someSize);
__attribute__((cleanup(free_void))) void *data2 = malloc(someSize);
__attribute__((cleanup(free_double))) double *result = malloc(resultSize);
__attribute__((cleanup(free_double))) double *buffer = malloc(buffSize);
return 0;
}
你可以用宏来简化这个:
static inline void free_void(void **value) { free(*value); }
#define cleanup__void_ptr __attribute__((cleanup(free_void))) void *
static inline void free_double(double **value) { free(*value); }
#define cleanup__double_ptr __attribute__((cleanup(free_double))) double *
int main()
{
/* Return any time you like and allocated things get freed */
cleanup__void_ptr data1 = malloc(someSize);
cleanup__void_ptr data2 = malloc(someSize);
if(!data2) {
return 0;
/* calls `free()` on data1 and data2. */
}
cleanup__double_ptr result = malloc(resultSize);
cleanup__double_ptr buffer = malloc(buffSize);
return 0;
/* calls `free()` on data1, data2, result and buffer */
}
我强烈建议不要使用 setjmp()
/longjmp()
,而是将清理函数附加到任何已分配的资源,这样您就可以在任何地方使用 return
语句。函数的任何调用者都应该检查 return 值。如果错误提示return立即处理,或者处理异常。这样你就不需要 setjmp()
/longjmp()
.