1 什么是单出口函数
在很多大型软件公司里,为保证软件质量,通常要求程序员在编写函数的时候,使函数尽量短,并且只能是一个出口,称为所谓的单出口函数。对于一条路走到黑的函数,必须在函数结尾处return,就是最简单的单出口示例:
bool myfunc1()
{
do_first_thing();
do_second_thing();
do_third_thing();
return true;
}
请注意这个函数本身就设计的不好,大部分的编程规范都要求一个函数只做一件事。
但是很多时候,函数的逻辑并不会这么简单,一层一层的if else return让函数的出口变的比较难以控制,看下面这个例子
bool myfunc2(char* p,int i)
{
if (NULL == p)
return false;
if (0 > i)
return false;
if (true == do_first_thing())
{
if (...)
.....
else
return true;
}
else
{
if (do_second_thing())
return false;
}
for (int j = 0;j < 100;j++)
{
if (do_third_thing(j))
return false;
}
return true;
}
大量的return,会让程序的可控性降低,而且如果函数里有申请内存、句柄等资源,在函数退出时需要释放,或者在函数开始的地方获取了一个信号量,在每个异常分支都需要释放该信号量,这种代码结构就要让人抓狂了
2 第一种方法
在很多C语言的工程里,比较流行的单出口方法是使用goto,在每个出错和中途退出的地方,都给设置一个返回值,并goto到一个固定的地点,对函数里的申请的资源在return前做统一的释放
int myfunc3(char* p,int i)
{
char* p1 = NULL;
char* p2 = NULL;
int ret = 0;
if (NULL == p || 0 > i)
{
ret = -1;
goto out;
}
p1 = malloc(64);
if (true == do_first_thing())
{
if (...)
.....
else
{
ret = 0;
goto out;
}
}
else
{
p2 = malloc(128);
if (do_second_thing())
{
ret = -2;
goto out;
}
}
for (int j = 0;j < 100;j++)
{
if (do_third_thing(j))
{
ret = -3;
goto out;
}
}
out:
if (p1)
free(p1);
if (p2)
free(p2)
return ret;
}
在函数的结尾处,对每个函数里可能申请的资源做判断,如果需要释放,在return前统一释放。
这种方法比较直观,不过很多程序员是有goto恐惧症的,看到goto就有一种强烈的欲望要把它干掉。
所以请看下面一种方法
3 第二种方法
int myfunc4(char* p,int i)
{
char* p1 = NULL;
char* p2 = NULL;
int ret = 0;
do
{
if (NULL == p || 0 > i)
{
ret = -1;
break;
}
p1 = malloc(64);
if (true == do_first_thing())
{
if (...)
.....
else
{
ret = 0;
break;
}
}
else
{
p2 = malloc(128);
if (do_second_thing())
{
ret = -2;
break;
}
}
for (int j = 0;j < 100;j++)
{
if (do_third_thing(j))
{
ret = -3;
break;
}
}
} while(0);
if (p1)
free(p1);
if (p2)
free(p2)
return ret;
}
将函数的工作体用do while(0)包裹起来,一旦要返回就立刻break。觉得每个函数里都要看到一个do while很别扭?
使用下面几个宏吧:
#define FUNCTION_BEGIN do \
{
#define FUNCTION_END }while(0);
#define GOTO_END break;