据我了解,对
assert(e)
的调用(其中 e
是布尔表达式)执行的操作大致类似于
if (!e) {
printf("%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, e);
abort();
}
如果给定的表达式不为真,程序会突然终止。
另一方面,我可以不使用
assert
,而是写类似的东西
if (!e) {
fprintf(stderr, "custom error message.\n");
exit(1);
}
天真地感觉这是一件更干净、更好的事情。
忽略可以使用
assert
标志全局关闭 NDEBUG
,您认为一个相对于另一个还有什么其他优势?我的区别是否正确,或者两者之间是否存在我不知道的概念差异,使得这两种条件程序终止方式都有自己的利基用例?如果是后者,请解释一下。谢谢你。
assert
最大的优点就是意图明确。如果您看到 assert(some_condition)
,那么您就知道作者的意图是什么(即,some_condition
始终是 true
)。使用您的内联版本,在我实际阅读您的 if
块并意识到您将显示错误消息并终止程序之前,我无法假设意图。
不太重要的原因包括
assert
减少了复制/粘贴错误,some_condition
自动转换为字符串(包括保存变量名称),并且工具可以理解它。
您认为其中一个相对于另一个还有什么其他优势?
使用宏是因为您希望能够通过条件编译删除它。换句话说,您甚至不希望代码出现在二进制文件中。
我的区别是否正确,或者两者之间是否存在我不知道的概念差异,使得这两种条件程序终止方式都有自己的利基用例?
嗯,即使您使用
exit()
作为“不成功”退出代码,abort()
和 1
的行为也不同。后者的目的是立即终止程序而不进行进一步的工作,并可能触发调试提示或保存进程空间的图像(尽管它的确切作用取决于提供它的供应商)。前者通过atexit()
调用注册的函数。还有其他停止方法,请参阅 quick_exit()
和 _Exit()
。
对于 C++,对于行为差异有更多的考虑,例如堆栈帧中变量的析构函数是否运行,全局析构函数是否运行,如果执行此操作时抛出异常会发生什么,等等。
'assert'是一个代码自动测试工具。有时,即使满足导致执行断言的条件,程序也不应该停止在客户端(发布版本)上的工作。 例如:
switch(color)
{
case red:
//...
break;
case green:
//...
break;
default:
assert(false && "unexpected color value. 'enum color' was modified?");
}
另一个例子:
if ( OkOrFailEnum::OK != result )
{
assert(false && "data access fail");
throw std::runtime_error("Can not get index " + std::to_string(index));
}
同时,‘assert’是一个代码注释工具。
inline unsigned Sum(unsigned* pPos, unsigned* pOffset)
{
assert(nullptr != pPos); // \
assert(nullptr != pOffset); // | preconditions
assert(*pPos + *pOffset < *pOffset && "Overflow?"); // /
return *pPos + *pOffset;
}
断言:
assert(*pPos + *pOffset < *pOffset && "Overflow?");
意味着 Sum(..) 函数在处理大和时无法正常工作,并且必须在调用该函数之前进行一些检查。