关于c++中的assert语句

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于c++中的assert语句相关的知识,希望对你有一定的参考价值。

说句心里话,我很少使用这句,一般都是写成if(p==NULL)之类的。
在面试的时候被鄙视了……
我想问问用assert而不是if,有什么好处?

assert一般是一个条件编译的宏,什么意思呢?简单说来,如果写成if,那么程序执行的时候需要额外开销来进行判断,如果调用很多次,将会影响程序性能,而实际上有一些判断,在调试的时候判断就可以了,等编译成为真正的线上产品的时候,其实是不需要的,因为如果调试的时候没有问题,最后就不会有问题。再简单点说,就是你自己写代码,调试的时候,你自己想做一些检查,而当你把你的程序拿给别人使用的时候,你想去掉那些检查从而尽可能提高执行速度,就用assert。具体就是通过条件编译来实现的,通过编译参数控制,调试的时候把assert当if使用,成品的时候不编译那段代码。

另外,assert还有一个好处就是assert失败是能自动输出更详细的出错信息,从而看出是哪里出错了,以便检查代码。如果用if,只能一个一个自己写,比如if(...) printf(...); 非常麻烦
参考技术A C里用法:
使用断言可以创建更稳定,品质更好且不易于出错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。单元测试必须使用断言(Junit/JunitX)。
除了类型检查和单元测试外,断言还提供了一种确定各种特性是否在程序中得到维护的极好的方法。
使用断言使我们向按契约式设计更近了一步。
断言特性:
前置条件断言:代码执行之前必须具备的特性
后置条件断言:代码执行之后必须具备的特性
前后不变断言:代码执行前后不能变化的特性
使用方式:
断言可以有两种形式
1.assert Expression1
2.assert Expression1:Expression2
其中Expression1应该总是一个布尔值,Expression2是断言失败时输出的失败消息的字符串。如果Expression1为假,则抛出一个 AssertionError,这是一个错误,而不是一个异常,也就是说是一个不可控制异常(unchecked Exception),AssertionError由于是错误,所以可以不捕获,但不推荐这样做,因为那样会使你的系统进入不稳定状态。
java断言:
断言在默认情况下是关闭的,要在编译时启用断言,需要使用source1.4标记 既javac source1.4 Test.java ,在运行时启用断言需要使用 -ea参数 。要在系统类中启用和禁用断言可以使用 -ea和 -dsa参数。
参考技术B 在真正的大型程序中,出问题的概率是非常大的,而出了问题如何解决,有时比较困扰,因为代码可能几百万行
就算你写if(p==NULL)也只是做的一些规避
正确的做法是加assert目的就在于,当条件是false时,assert让程序中止,输出错误所在的代码块,这样才方便分析和解决问题。
断言常用在程序开发过程中,程序员不想写更多的异常处理代码,又想立即定位到异常的位置,在DEBUG模式下断言就起到了关键的作用,大大提高开发效率。
参考技术C 哈哈!果断被鄙视啊!

assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:#include <assert.h>
void assert( int expression );

assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
参考技术D 现在假设有如下函数,
void Func(int *p)



当调用函数时,在参数检测这一块有两种做法:
一是调用前检测(属于规范的做法);
二是寄希望于函数自己检测;

如果不在Func函数里检测参数,那么对于后者来说,当错误发生时,想快速准确地定位通常都是比较困难的;但如果在Func里加上if语句判断,那对于前者,又会有重复检测的开销.
而assert正好可以解决这个问题,assert只在debug版本中有效,在release版本中为空宏.

在 C++ 中实现无操作语句的可移植方式是啥?

【中文标题】在 C++ 中实现无操作语句的可移植方式是啥?【英文标题】:What's a portable way to implement no-op statement in C++?在 C++ 中实现无操作语句的可移植方式是什么? 【发布时间】:2011-12-20 04:29:30 【问题描述】:

有时需要在 C++ 中使用无操作语句。例如,当实现在非调试配置中禁用的assert() 时(另见this question):

#ifdef _DEBUG
#define assert(x) if( !x )  \
                     ThrowExcepion(__FILE__, __LINE__);\
                   else \
                     //noop here \
                  
#else
#define assert(x) //noop here
#endif

到目前为止,我认为正确的方法是使用(void)0; 进行无操作:

(void)0;

但是我怀疑它可能会在某些编译器上触发警告 - 类似于 C4555: expression has no effect; expected expression with side-effect Visual C++ 警告,它不会针对这种特殊情况发出,但在没有强制转换为 void 时会发出。

它是通用便携的吗?有没有更好的办法?

【问题讨论】:

拥有基于 DEBUG/RELEASE 改变程序行为的宏通常不是一个好主意...您最终可能会遇到 DEBUG(易于使用) 构建行为正确,但 RELEASE 构建没有。作为一个无操作:; 应该这样做,(void)0;(你的宏不应该包含;,应该由调用者添加) @David Rodríguez - dribeas:是的,我知道,但在非调试版本中禁用断言是一种普遍的做法,我仅将其用作示例。 我不明白为什么你需要在一个空的 else 块中插入一个 no-op。如果您想稍后填写,可以将 else 块留空。 @ziu 他在谈论#define assert(x) //noop here @sstn - 这不是模棱两可的。 C 语法指定else 与最里面的if 绑定。如果您添加不正确的缩进,它看起来模棱两可,但事实并非如此。 (警告设置足够高的编译器可能会声称它不明确,但在这种情况下,最好的解决方案是将编译器警告调整为您要编写的代码,或使用do if(!x) ThrowException(__FILE__, __LINE__); while(0)。) 【参考方案1】:

我参加这个聚会有点晚了,但我需要一个 Arduino 项目中的 loop() 相同,所有处理都在定时器中断服务例程 (ISR) 中完成。发现内联汇编代码在没有定义函数的情况下对我有用:

void loop()
  __asm__("nop\n\t");             // Do nothing.

【讨论】:

【参考方案2】:

我认为这里的目标以及不将宏定义为空的原因是要求用户添加;。为此,任何声明合法的地方,(void)0(或((void)0),或其他变体)都可以。

我发现了这个问题,因为我需要在 global 范围内做同样的事情,其中​​一个普通的旧语句是非法的。幸运的是,C++11 为我们提供了一个替代方案:static_assert(true, "NO OP")。这可以在任何地方使用,并实现了我在宏之后需要; 的目标。 (在我的例子中,宏是用于解析源文件的代码生成工具的标记,因此当将代码编译为 C++ 时,它总是是 NO-OP。)

【讨论】:

【参考方案3】:

然后呢:

#define NOP() ((void)0;)

或者只是

#define NOP() (;)

【讨论】:

这款便携吗?【参考方案4】:

优化不会省略这段代码

static void nop_func()     
typedef void (*nop_func_t)();
static nop_func_t nop = &nop_func;

for (...)

    nop();

【讨论】:

@sharptooh 是的,通过不可内联的变量指针调用 func 是否禁止优化器推断被调用函数并内联调用? 这将被优化掉,我只是查看了编译器的输出,不管你说的是静态的、内联的还是什么都没有。但是您可以通过说 void nop_func()asm(""); 来阻止所有优化【参考方案5】:
    inline void noop( ) 

自我记录

【讨论】:

这个答案如何“它是通用便携的吗?有没有更好的方法?” 通常避免仅使用代码的答案。考虑添加有助于解释代码的description。我认为您可以比“自我记录” 做得更好。谢谢。 如果读者不理解代码的含义,我建议使用 en.cppreference.com 或 www.cplusplus.com。如果使用 C++ 编译器,它显然是可移植的,并且它是否比其他方式“更好”是严格的意见;读者可以随心所欲地使用或不使用它。 这可能会导致函数调用,而这几乎不是“无操作”。 哪个 C++ 编译器在优化时会为此生成代码?【参考方案6】:

我推荐使用:

static_cast<void> (0)   

【讨论】:

【参考方案7】:

AFAIK,它是通用便携的。

#define MYDEFINE()

也可以。

另一个选项可能是这样的:

void noop(...) 
#define MYDEFINE() noop()

但是,我会坚持使用 (void)0 或使用像 __noop 这样的内部函数

【讨论】:

#define assert 在您自己的代码中是未定义的行为;标准说明了assert 的含义。 +1 @JamesKanze 我并不是要从字面上定义断言名称,它是用户定义的名称占位符。澄清。 我也有同样的怀疑,但你永远不知道有些读者会读到什么,这对你来说是一个纯粹随意的选择:-)。 (标准的assert 通常使用(void)0,因为它需要可用作子表达式;例如someCondition || assert(otherCondition)。) @JamesKanze 我完全同意。【参考方案8】:

我怀疑它可能会在某些编译器上触发警告

不太可能,因为((void)0) 是标准assert 宏在定义NDEBUG 时扩展的内容。因此,每当编译包含断言的代码以供发布时,任何为其发出警告的编译器都会发出警告。我希望用户会认为这是一个错误。

我想编译器可以通过警告您的提案(void)0 来避免这个问题,同时只特别处理((void)0)。所以你最好使用((void)0),但我对此表示怀疑。

一般来说,将某些东西强制转换为 void,无论有没有额外的封闭括号,惯用的意思是“忽略这个”。例如,在 C 代码中,将函数参数转换为 void 以抑制未使用变量的警告。所以在这个分数上,一个警告的编译器也会相当不受欢迎,因为抑制一个警告只会给你另一个警告。

请注意,在 C++ 中,允许标准头文件相互包含。因此,如果您使用 any 标准标头,assert 可能已被它定义。因此,您的代码在该帐户上是不可移植的。如果您说的是“通用可移植”,则通常应将任何标准标头中定义的任何宏视为保留标识符。您可以取消定义它,但为您自己的断言使用不同的名称会更明智。我知道这只是一个例子,但我不明白你为什么要以“通用可移植”的方式定义 assert,因为所有 C++ 实现都已经有了它,而且它并不能满足你的需求在这里定义它。

【讨论】:

【参考方案9】:

do while(0) 怎么样?是的,它添加了代码,但我相信今天的大多数编译器都能够优化它。

【讨论】:

【参考方案10】:

最简单的无操作就是根本没有代码:

#define noop

然后用户代码将有:

if (condition) noop; else do_something();

您提到的替代方案也是无操作:(void)0;,但如果您要在宏中使用它,您应该将; 放在一边,让调用者添加:

#define noop (void)0
if (condition) noop; else do_something();

(如果; 是宏的一部分,那么那里会有一个额外的;

【讨论】:

【参考方案11】:

;被视为标准无操作。请注意,编译器可能不会从中生成任何代码。

【讨论】:

嗯,链接问题的答案表明这不是一个好主意。

以上是关于关于c++中的assert语句的主要内容,如果未能解决你的问题,请参考以下文章

C++中的assert

C语言C++中assert的用法

您可以使用 assert 来测试 C++ 中的类型定义吗?

assert函数和捕获异常

Swift:Assert 语句中的可选元组类型

java中的assert