处理可能按顺序发生的多个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处理这些代码的代码,在那里你只清除一个地方的所有mallocs。代码不长,但我不喜欢使用gotos。
有比这两种方法更好的方法吗?
也许问题在于设计首先。在设计函数时,最小化多个mallocs是否有经验法则?
编辑: 还有另一种我见过和使用过的方式。您不保留使用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;
}
答案 0 :(得分:4)
这是一个可能的解决方案:
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;
}
我不建议你的第二个(&#34;邪恶&#34; goto避免)解决方案。 goto解决方案更复杂。
BTW释放空指针是安全的,因此
if(pBufY != NULL) // NULL and not null
free(pBufY); // not y but pBufY
可以替换为:
free(pBufY);
答案 1 :(得分:1)
就个人而言,我更喜欢使用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;
}
如果您需要分配的内存来进行计算以获得进一步分配的大小,那么您的函数可能非常复杂,无论如何都应该重构。
答案 2 :(得分:1)
好问题! (我以为我会找到它的一个骗局,但不,很奇怪,C中的这个重要方面似乎从来没有真正问过)
我发现有两个问题涉及这个领域:
这些主要关注转到的方式。首先让我们介绍一下。
转到方式
如果您的代码依赖于分配几个必须在之后发布的资源,您可以使用如下模式:
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;
}
模式得名的明显问题是潜在的深度嵌套(代码看起来像箭头),为什么这种模式不那么受欢迎。
其他方式
我可以想到分配和释放资源的要求(除了您在问题中详细说明的标记变体)。当你没有这样的约束时,你有更多的自由,你可以看到其他问题和答案中描述的一些好的方法。
如果资源不相互依赖,您也可以使用其他模式(例如第一个代码示例提供的提前返回),但这两个模式可以正确处理如果需要,可以使用资源依赖(即,resource2只能在有效资源1之上分配),并且如果给定资源的自由函数不能处理失败的分配返回。
答案 3 :(得分:0)
在我看来,最合适的方法是将核心功能转移到一个单独的功能:
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,从而将内存分配与实际算法分开,但有时这是不可能的
请注意,免费保证在您向其传递空指针时它什么都不做。
编辑:
请注意,如果在doStuff
内进行分配,则需要传递指针指针!