断言(assert宏)的副作用
我的确对#define的很多种用法都深恶痛绝,唯对定义在中的assert宏情有独钟。说句实话,我鼓励大家多多使用它--前提是用好它。但问题就在于能不能用好它。
实现的方式固然百家争鸣,不过assert宏多数情况下和下面的定义相差不远:
gotcha28/myassert.h
#ifndef NDEBUG
#define assert(e) ((e) \
? ((void)0) \
:__assert_failed(#e,__FILE__,__LINE__) )
#else
#define assert(e) ((void)0)
#endif
如果NDEBUG有定义,那么我们就没有在调试模式下,assert宏就会展开成一个空操作(no-op)。否则,我们就处在调试模式下,(在此特定实现中)assert宏就会展开成一个条件表达式以对某特定条件进行(谓词)测试。若该条件测试结果为false,则我们生成一条诊断信息并调用abort(以无条件强制终止程序运行)。
使用assert宏优于使用注释来文档化前置条件、后置条件及不变量(invariant)。一条assert宏,在生效时,会对执行特定条件来个运行时校验,所以不会被轻轻松松地被当作一个注释而被无视(参见常见错误1)。与注释不同的是,由于违反了assert宏的正确性校验的错误通常来说都被更正了,因为"调用abort"这种后果会使得"代码需要维护"这件事必须马上完成:
//gotcha28/myassert.cpp
template
void doit( Cont &c, int index ) {
assert( index >= 0 && index < c.size() ); // #1
assert( process( c[index] ) ); // #2
// ...
}
在上面这段代码中,我们演示了几个使用assert宏的过程中犯下的用法错误。标了#2的那行代码是明显的误用,因为我们在调用一个函数,而这个函数被放到assert宏中去以后可能会有其副作用。这段代码的行为会随着NDEBUG符号有否被定义而有本质的不同。 这种assert宏的用法会导致在调试模式下代码行为完全正确,而把调试模式关掉后原有的软件缺陷就复现了。于是你会又打开调试模式,缺陷又消失了。然后你再关掉调试模式,结果……(死循环!)。
标了#1的那行代码错得更微妙。Cont class的成员函数size很有可能是一个常量成员函数,因此,它不会有副作用,对吗?错!除了size这个名字的习惯意义之外,我们找不到任何理由来保证该成员函数具有常量语义。就算它真的是常量成员函数,也不能保证对它的调用就(对代码的行为)没有副作用。(再退一步讲)就算(执行完size函数后)c的逻辑状态没有改变,它的物理状态仍然可能发生了变化(参见常见错误82)。最后,我们可不要忘了使用assert宏就是为了找出代码缺陷。即使调用size函数的本意并非要向代码行为中引入什么可觉察的(变化)效应,它的实现仍然可能包含缺陷(使得这种效应出现)。我们当然希望对assert宏的使用会将代码缺陷暴露于光天化日,而不是将它们藏匿起来。正确的assert宏的用法会避免其条件语句带有任何潜在的副作用:
template
void doit( Cont &c, int index ) {
const int size = c.size(); // 译注:
避免了size未被调用的潜在副作用
assert( index >= 0 && index < size ); // 正确
// ...
}
显然,assert宏并非万金油,但它的确在位于注释和异常之间的某个位置扮演了代码文档化及捕捉非法行为的适当角色。其最大问题在于它到底是一个伪函数,因此它也(无可避免地)带着前面的条款中描述的有关伪函数的种种先天不足(参见常见错误26)。好在它是一个标准化了的伪函数,这也就暗示着其不足之处已为世人熟知。只要使用时多长个心眼,assert宏就能为我们造福。
分享到:
相关推荐
在讲解之前,我们先来对断言做一个基本的介绍,让大家对断言有一个大致的了解。
eclipse如何开启断言assert调试,文档详细的讲解了配置的流程。
C语言断言assert的用法.pdf
可以为Java复杂的测试提供简单的方法!可以加快开发进度
VC中如何使用ASSERT断言 VC中如何使用ASSERT断言
使用springboot框架快速搭建,封装自定义断言做业务校验,公共异常处理打印日志, 更简单的业务判断处理
debug_assert, 简单灵活和模块化断言宏 debug_assert debug_assert是只提供一个非常灵活的DEBUG_ASSERT() 宏的简单。C 11.头库。 你自己写了多少次断言宏,因为 assert() 是全局控制的,不能在程序的某些部分启用? ...
这时候断言assert 就显得非常有用。 python assert断言是声明布尔值必须为真的判定,如果发生异常就说明表达式为假。 可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常。 assert...
这为Rust编程语言提供了一个改进的assert宏,从而无需使用assert_eq! 同时还提供!= , >等的等效项。 基本思想是,如果为宏提供了格式为a == b的条件,它将执行assert_eq! 会执行,即,如果断言失败,则打印出a和b...
。。。
深入探讨C++中的引用和ASSERT断言,对引用和断言的理解会有很大的帮助
断言函数assert[归纳].pdf
assert()宏是用于保证满足某个特定条件。 用法是: assert(表达式); 如果表达式的值为假,整个程序将退出,并输出一条错误信息。如果表达式的值为真则继续执行后面的语句。 使用这个宏前需要包含头文件assert.h ...
python assert断言的作用 python assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为 下面通过实例代码介绍下python ...
1-assert(断言).pdf