单出口函数的代码结构

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;






  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
汇编语言程序代码通常具有明确的结构,它直接对应计算机硬件指令。一个典型的汇编语言程序包括以下几个部分: 1. **标签(Labels)**:汇编程序中经常使用标签来标识特定的位置,如函数开始、循环或条件分支的起点。它们提供了一个名称给指令或内存地址,方便程序的阅读和跳转。 2. **指令集(Instructions)**:汇编指令是基本的操作,比如加载数据、存储数据、算术运算、逻辑运算、跳转等。每条指令由操作码(Opcode)和操作数(Operands)组成,操作码指示了执行的操作,而操作数提供了执行操作所需的数据。 3. **数据段(Data Segments)**:这部分存放全局变量、常量、数组等内容,数据以二进制形式存储,通常有固定的偏移地址。 4. **代码段(Code Segments)**:这部分存放程序的执行代码,包括函数的入口点(Entry Point)和出口点(Exit Point),以及调用其他函数的指令。 5. **子程序(Functions)**:为了组织代码并复用,汇编程序员会定义函数,包含一组相关的指令。函数有开始和结束标记,以及可能的参数传递和返回值。 6. **调用约定(Calling Convention)**:确定函数如何在内存中传递参数和返回结果,不同的系统可能有不同的调用约定。 7. **错误处理(Error Handling)**:汇编程序中可能包含异常处理指令或跳转到特定的错误处理区域。 8. **结束标志(End of Program)**:程序的结尾通常有一个终止指令,告诉操作系统或执行环境该程序已结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值