《码农修行》法则07:避免过度防御

《码农修行》法则07:避免过度防御


非法指针是C/C++程序中最令人头痛的问题之一。你也许会有类似的习惯,在函数入口即对指针的合法性进行检查。代码如下。

即使在调用该函数前已经做过一次检查。代码如下。

这两处检查都属于过度防御,完全没有必要。首先,参数的合法性应该由调用者保证。可以参考一些参数为指针的API,比如strncpy,当对其强制传入一个空指针时,程序并不会悄无声息,而是会抛出异常。

因为这样的参数已经违反了接口的规约,函数内部不知道该如何处理,只能抛出异常让程序崩溃。

此外,就指针而言,哪些属于非法指针,哪些属于合法指针其实很难界定。对于上面的例子,假设调用者强制传入一个“1”,其实程序也会崩溃。

空指针只是非法指针中的一种而已,对于其他情况更是防不胜防,关键要划清责任范围。

过度防御还会造成大量冗余代码,因为很多类似这样的分支是永远不会执行的。

更令人沮丧的是,过度防御可能会增加某些问题定位的难度,因为没有在出现问题的“第一现场”暴露问题。假设一个交易系统中定义了一个带有过度防御功能的StrNCpy函数。代码如下。

在交易成功后需要保存订单,SaveOrder中使用了StrNCpy进行字符串复制,组装了一个Order数据结构,并调用DB_SaveOrder存入数据库。代码如下。

假设由于某个bug(漏洞)导致上层传入的“公司名称”companyName为一个空指针NULL。由于StrNCpy的过度防御,该函数“悄无声息”地执行完毕,让SaveOrder“误以为”数据复制成功,并将信息写入数据库。

当消费者想要根据这张订单开具发票时,结果发票开具失败。

通过分析发现数据库中该订单的“公司名称”字段没有内容,这是导致发票开具失败的直接原因,因为购方企业名称是发票开具流程中的必填参数。

这个字段为什么没有内容?是一开始就没有?还是后来被误删除了?这需要翻阅大量的日志,才能分析定位该问题。但如果改用符合接口规约的API函数,那么在保存订单时程序就会抛出异常,及时暴露问题,像上述那样不完整的订单信息就不会写到数据库中。以下代码中将StrNCpy替换为strncpy。

那么正确的保护方法应该是什么?答案是:在可能出错的地方进行保护,做“正当防卫”。比如上面的例子中,假设入参name是通过new动态申请的。

new申请动态内存可能会失败,因此需要在申请返回后进行保护。

当然Java语言情况稍好一些,new不会返回null,因此不需要类似的保护。但有一种情况需要注意,如果一个对象是通过某个方法获取到的,就需要关注该方法是否可能失败,如果是就需要在返回的地方加以保护。

更多内容敬请关注我的新书《码农修行》

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值