建议用什么方法来处理以下代码中可能按顺序发生的多个malloc错误?
bool myFunc(int x, int y)
{
int *pBufX = null;
int *pBufY = null;
if((x <= 0) || (y <= 0))
{
return false;
}
pBufX = (int*)malloc(sizeof(int) * x);
if(pBufX == null)
{
return false;
}
pBufY = (int*)malloc(sizeof(int) * y);
if(pBufY == null)
{
free(pBufX) //free the previously allocated pBufX
return false;
}
//do something useful
free(pBufX);
free(pBufY);
return true;
}
这种方法的问题是,如果malloc的数量很高,您可能会忘记释放一些,从而导致内存泄漏。此外,如果出现错误时需要输出某种日志,则代码会变得非常长。
我看到过用goto处理这些问题的代码,在goto中,您只需在一个地方清除一次所有malloc。代码不长,但我不喜欢使用gotos。
有比这两种方法更好的方法吗?
也许问题出在设计上。当涉及到最小化多个malloc时,在设计函数时有经验法则吗?
编辑:我看到并使用了另一种方式。不使用goto,而是保留程序的状态,只有在状态为OK时才继续。类似于goto,但不使用goto。但这会增加if语句的数量,这可能会使代码运行速度变慢。
bool myFunc(int x, int y)
{
int *pBufX = null;
int *pBufY = null;
bool bRet = true;
if((x <= 0) || (y <= 0))
{
return false;
}
pBufX = (int*)malloc(sizeof(int) * x);
if(pBufX == null)
{
bRet = false;
}
if(bRet == true)
{
pBufY = (int*)malloc(sizeof(int) * y);
if(pBufY == null)
{
bRet = false;
}
}
//do something useful
if(pBufX != null)
free(pBufX);
if(pBufY != null)
free(pBufY);
return bRet;
}
这是一个可能的解决方案:
bool myFunc(int x, int y)
{
int returnvalue = false;
int *pBufX = NULL; // << null -> NULL
int *pBufY = NULL;
if((x <= 0) || (y <= 0))
{
goto fail;
}
pBufX = (int*)malloc(sizeof(int) * x);
if(pBufX == null)
{
goto fail;
}
pBufY = (int*)malloc(sizeof(int) * y);
if(pBufY == null)
{
goto fail;
}
//do something useful
returnvalue = true;
fail:
free(pBufX); // <<< free(x) -> free(pBufX)
free(pBufY); // <<< free(y) -> free(pBufY)
return returnvalue;
}
我不会再推荐你的第二个("邪恶的"后藤回避)解决方案。它比goto解决方案更复杂。
顺便说一句,释放空指针是安全的,因此
if(pBufY != NULL) // NULL and not null
free(pBufY); // not y but pBufY
可替换为:
free(pBufY);
就我个人而言,我更喜欢使用goto。但由于free(NULL)
是安全的,您通常可以提前完成所有分配:
int *a = NULL;
int *b = NULL;
a = malloc( sizeof *a * x);
b = malloc( sizeof *b * y);
if( a == NULL || b == NULL ) {
free(a);
free(b);
return false;
}
如果你需要分配的内存来进行计算,以获得进一步分配的大小,那么你的函数可能足够复杂,无论如何都应该重构它。
好问题!(我以为我会发现它是一个骗局,但不,奇怪的是,C中如此重要的方面以前似乎从未真正问过)
我发现有两个问题涵盖了这个领域:
- 在C中有效使用goto进行错误管理
- 在直接C程序中,有什么好的错误处理习惯用法吗
这些主要集中在goto方式上。所以,首先让我们来介绍一下。
goto方式
如果你有一个依赖于分配几个资源的代码,这些资源必须在之后发布,你可能会使用下面的模式:
int function(void){
res_type_1 *resource1;
res_type_2 *resource2;
resource1 = allocate_res_type_1();
if (resource1 == NULL){ goto fail1; }
resource2 = allocate_res_type_2();
if (resource2 == NULL){ goto fail2; }
/* Main logic, may have failure exits to fail3 */
return SUCCESS;
fail3:
free_res_type_2(resource2);
fail2:
free_res_type_1(resource1);
fail1:
return FAIL;
}
你可以在优秀的Regehr博客上阅读更多关于这种方法的信息:http://blog.regehr.org/archives/894也指向经常使用这种模式的Linux内核本身。
箭头代码
这是另一种可能的方法。上面的例子看起来像一个"箭头"模式:
int function(void){
res_type_1 *resource1;
res_type_2 *resource2;
int ret = FAIL;
resource1 = allocate_res_type_1();
if (resource1 != NULL){
resource2 = allocate_res_type_2();
if (resource2 != NULL){
/* Main logic, should set ret = SUCCESS; if succeeds */
free_res_type_2(resource2);
}
free_res_type_1(resource1);
}
return ret;
}
该模式命名的明显问题是潜在的深度嵌套(代码看起来像箭头),这就是为什么该模式不太受欢迎的原因。
其他方式
关于分配和释放资源的要求,我想不出太多(除了你在问题中详细描述的标记变体)。当你没有这样的约束时,你会有更多的自由,你可以在其他问题和答案中看到一些很好的方法。
如果资源不相互依赖,您也可以使用其他模式(例如您的第一个代码示例提供的早期返回),但如果您需要,这两种模式可以正确处理资源依赖性(即,资源2只能在有效资源1之上分配),如果给定资源的空闲函数没有处理失败分配的返回。
在我看来,最合适的方法是将核心功能转移到一个单独的功能:
inline bool doStuff (int x, int y, int* pBufX, int* pBufy)
bool myFunc (int x, int y)
{
bool result;
int *pBufX = NULL;
int *pBufY = NULL;
/* do allocations here if possible */
result = doStuff(x, y, pBufX, pBufY); // actual algorithm
free(pBufX);
free(pBufY);
return result;
}
现在,您只需要在出现错误时从doStuff
返回,而无需担心解除分配。理想情况下,您可以在包装器函数中执行malloc,从而将内存分配与实际算法分离,但有时这是不可能的
请注意,free保证在您将空指针传递给它的情况下它什么都不做
编辑:
请注意,如果在doStuff
中进行分配,则需要将指针传递给指针!