详解assert与if
一. assert的作用
和 if
的作用基本无异,都用来检测一些边界条件和进行安全性检查,如:
- 指针是否为空?
- 被除数是否为 0?
- 函数调用的返回结果是否有效?
- 打开一个文件是否成功?
使用格式:assert(exp);
assert
会计算表达式 expression
,如果其值为假(即为0),那么它先向 stderr
打印一条出错信息,然后通过调用 abort
来终止程序运行 。
从功能上而言,下面的两种写法等效:
1 | assert(0 != b); |
同时需要注意,assert
是宏,而非函数:
1 |
从上面assert的宏可看出:
- 如果定义了宏
NDEBUG
,那么assert()
宏将不做什么动作,也就是相当于一条空语句:(void)0;
,当在 release 阶段编译代码的时候,都会在编译选项中(Makefile)定义这个宏 。- 如果没有定义宏
NDEBUG
,那么assert()
宏将会把一些检查代码进行替换,我们在开发阶段执行 debug 模式编译时,一般都会屏蔽掉这NDEBUG
这个宏 。一般来说断言 assert() 是仅在 Debug 版本起作用的宏。在发布版本时,我们不应该再依赖 assert() 宏,因为程序一旦出错,assert() 会抛出一段用户看不懂的提示信息,并毫无预警地终止程序执行,这样会严重影响软件的用户体验,所以在发布模式下应该让assert()失效。另外在程序中频繁的调用 assert() 会影响程序的性能,增加额外的开销。
二. assert与if的区别
在功能上没有明显区别。 二者在工程代码中都很常见,没有对错之分,更多只是编程风格和习惯上的差异。
(1) assert 支持者
我作为
my_concat()
函数的实现者,目的是拼接字符串,那么传入的参数必须是合法有效的,调用者需要负责这件事 。如果传入的参数无效,我会表示十分的惊讶!怎么办:崩溃给你看!(2)if 支持者
我写的
my_concat()
函数十分的健壮,我就预料到调用者会乱搞,故意的传入一些无效参数,来测试我的编码水平。没事,来吧,我可以处理任何情况!
从上文我们得知,assert
仅在 debug 模式下有效, release 模式下不会作用。而 debug 模式是用来排除 bug 的,即排除 非法情况 ,将 所有可能存在 的非法情况全部排除后,才能发行(release)。而如果试图用 if
来判断非法情况并处理错误(以此提高所谓的程序 “健壮性” ),则很可能会隐藏本来就需要排查的非法情况。而这样的程序发行之后,随时会因为某些极端情况而崩溃。
非法情况: 即逻辑层面的 bug 。非法情况是完全不应该出现的情况,逻辑上不允许它的存在。如传入的参数为空指针(在不同的场景下有不同的要求)。
错误情况: 是可以存在,且无法完全避免的情况,在逻辑允许之内。如
malloc()
未申请到内存,返回NULL
。
assert
便是用来处理非法情况(验证有效性)它最大作用就是:在开发阶段,让我们的程序尽可能地 crash。每一次的 crash,都意味着代码中存在着 bug,需要去修正。当我们写下一个 assert 断言的时候,就说明:断言失败的这种情况是是不被允许存在的。必须保证断言成功,程序才能继续往下执行。
if
则是用来处理逻辑上各种可能出现的情况,包括错误情况。每一个分支都是合理的,是允许出现的,我们都要对这些分支进行处理。
三. 使用注意事项
-
在函数开始处检验传入参数的合法性
1
2
3
4
5
6
7int resetBufferSize(int nNewSize)
{
assert(nNewSize >= 0); //nNewSize<0是非法情况
assert(nNewSize <= MAX_BUFFER_SIZE);
if(nNewSize == 0){...} //nNewSize=0是可能的情况
else{...}
} -
每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败
1
2
3
4assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize); //不好
//========================================================
assert(nOffset >= 0);
assert(nOffset+nSize <= m_nInfomationSize); //好 -
不能使用改变环境的语句,因为assert只在DEBUG个生效,如果这么做,会使用程序在发行后遇到问题
1
2
3
4assert(i++ < 100); //错误
//========================
assert(i < 100)
i++; //正确 -
assert和后面的语句应空一行,以形成逻辑和视觉上的一致感
-
有的地方,assert不能代替条件过滤
这需要分清 非法情况 与 错误情况 。
如有错误,烦请读者指出。