kingofark's Ineffective C/C++:自白1:返回值的运用

Ineffective C/C++ : The Confession of A Novice
《Ineffective C/C++ :一个低手的自白》

by K ][ N G of @ R K

[声明:kingofark并非高手,在论述中所举的例子未必就可取,也未必就是很好的做法。所有例子仅仅是为了说明某些问题,并不具有代表性。kingofark自知才浅,欢迎大家提出批评,指出错误。]

Item 1: Return Values
自白1:返回值的运用


[问题]:试评价如下代码碎片。

/
// ... (在外部定义中)
// 有一组函数属于同一个系统,都以 OK 或者 NG 作为返回值
#define OK   0
#define NG  -1
int AFunction();  //返回OK或者NG

// ... (在某个函数中)
int Rtn;   //存放调用子函数的返回值
Rtn = OK;  //初始化
// ...
Rtn = AFunction();  //调用一个函数
if (Rtn == NG)
{
  //...(这里做一些错误处理)
  return Rtn;  //只要错了就向外退
}
// ...
return Rtn;  //oops! 当前函数返回
/


[kingofark的看法]:

首先需要说明的是,把返回值定成 OK、NG,以及让一组函数具有相同返回方式,这本身并非一个好主意(事实上,笔者认为这个设计很拙劣——但是,你知道,当你是一个软件工人的时候,并非什么东西都是你自己设计的),但这不是本条款所要描述的重点,因此对这些外部的背景条件我们不予讨论。

这个代码碎片至少存在三个问题:

1) 函数返回值的判断方式

/
if (Rtn == NG)
/

"if (Rtn == NG)"意味着:只要返回值不是NG,一律认为是成功。
而"if (Rtn != OK)"意味着:只要返回值不是OK,一律认为是失败。

前者意味着在子函数存在逻辑错误或者发生不可预料的错误时,子函数内部并未陷入代码编写者编写的失败执行路线导致返回NG,而这个子函数调用仍然被认为是成功的——因为此时子函数返回的很可能是OK,甚至是一个垃圾值(如果子函数内部用一个内部变量作为返回值的话),但它不等于NG!

后者附和了函数返回的意义,即只有在子函数内部确实走过了预想的正确执行路线,导致返回OK的情况下才承认调用的正确性,其它所有情况(包括函数失败返回NG以及其它任何不可预料的错误)都认为是失败——这样做的好处是:

    a) 便于调试:只要在函数内部没走到过预想的执行路线,就不返回OK,于是函数调用就被认为是失败的。由此往往能够发现一些函数内部的低级逻辑错误;

    b) 促使函数编写者考虑得尽量周全:如果真的是希望函数具有较高的容错性,请小心安排好返回OK。

2) 初始化的方式

/
Rtn = OK;  //初始化
/

把存放函数返回值的变量初始化为成功的返回值,意味着:

   a) 如果在后面的代码中没用到过这个变量,而又不小心用 "return Rtn;" 返回(从而使编译器也不会警告你这个变量没使用过),那么这个函数就会认为是成功调用的而不管其中发生了什么不可思议的事情,从而使得调试人员很难发现这个函数的问题(调试人员必须很仔细的考察与这个函数相关联的值和该函数产生的效果)。

   b) 编写代码者可能是个乐观主义者。乐观是好事,但并不是说乐观就不会犯错——我们要“严肃活泼”:-)

似乎写 "Rtn = NG;" 更好一些,因为这意味着:在函数中,只要没有将 Rtn 赋值为OK,那么在后面使用 "return Rtn;" 返回时,会返回NG从而引起你的注意,看看是不是什么地方编写错了(比如忘记使用这个变量)。

3) 返回的方式

/
return Rtn;  //oops! 当前函数返回
/

如果函数没有错误系统(即返回值涵盖了一系列设计好的错误码),似乎应该明确以 OK 或者 NG 返回,从而避免因为某种原因而返回垃圾值,比如

Rtn = Fun();  //误用:倒霉!Fun()恰好是一个可能返回多种错误码的函数
return Rtn;   //注意:其实这样返回本身就不好!

或者

//注意:这个写法太诡异,绝对不推荐使用!仅作为举例。
return (Rtn || Flag); //笔误:惨!本来想写 | 来着(其中flag = -1)
 

另外,还有一个需要注意的问题:一定要根据函数的返回值判断函数调用的情况!!

这好像是一句废话,但是就有人这么做:

/
//... (在与前面相同的系统中)
int myfun()
{
  if (...)
  {
    return 1; //一种返回情况
  }
  //...
  return 0;  //另一种返回情况
  //...
}

//...(在另外一个地方的代码中)
Rtn = myfun();
if (Rtn != OK)  //甚或是 if (Rtn == NG)
{
  //...
}
/

是的,一般来说,我们会把诸如OK这样的东西定义为0,但如果万一不是呢?如果后来系统改版,另一个高手决定OK应该等于100呢?

上面的代码中,或许myfun()的编写者仅仅只是忘记了在注释和文档中明确说明“0与该系统中的OK对应;-1与该系统中的NG对应”并且把 "return -1;" 误写为 "return 1;",但是,请铭记:永远只使用函数明确说明的返回值进行返回值判断,无论这个值是否“(应该)实际上与某个外部量相对应”。

只要你坚持使用函数明确说明的返回值进行返回值判断,那么无论是该函数的编写者忘记了说明,还是什么其它琐事,你的做法都是对的。如果你一看,心里想,哦,应该是编写者忘了说明,“本来应该”是使用OK/NG的,所以你就这样做了,那么当OK很不幸的被重新定义为100而该函数又没有做任何更改时,写错代码的就是你。

[kingofark的收获]:

1) 宁可错杀一千,不可放过一个。

2) 只写对的,不玩鬼的。

3) 理想与现实的差距在于:理想中的错误是你想得到的,而现实中的错误有些是你想不到的。

4) 当你调程序调不通的时候,请把你自己认为最不可能出错的地方罗列出来,然后逐个仔细检查。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了python应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值