C/C++开发,c/c++断言(预处理断言、编译断言、运行断言)

目录

一、预处理断言

二、编译断言

三、运行断言


        断言(assertion)是一种编程中常用的手段。在通常情况下,断言就是将一个返回值总是需要为真的判别式放在语句中,用于排除在设计的逻辑上不应该产生的情况。比如一个函数总需要输入在一定的范围内的参数,那么程序员就可以对该参数使用断言,以迫使在该参数发生异常的时候程序退出,从而避免程序陷入逻辑的混乱。

        从软件应用意义上讲,断言并不是正常程序所必需的,不过对于程序调试来说,通常断言能够帮助程序开发者快速定位那些违反了某些前提条件的程序错误。

一、预处理断言

        在 c/c++代码中,我们常能看到名为 errno 的预处理器宏:

        1)#error 是一种预编译器指示字,用于生成一个编译错误消息 。
        2)#error [message] //message为用户自定义的错误提示信息,可缺省。
        3)#error 编译指示字用于自定义程序员特有的编译错误消息。
        4)#error 可用于提示编译条件是否满足。编译过程中的任何错误意味着无法生成最终的可执行程序。

        它常用的用法就是,通过预处理指令#if和 #error的配合,可以让程序员在预处理阶段进行断言。例如我们可以在程序中判断某个自定义宏是否存储而进行断言提示:

#ifndef _MY_OPT_
#error "_MY_OPT_ not define in code, include <mydef.h> instead." 
#endif

        如果程序中没有包含需要的头文件<mydef.h>并进行编译,该头文件内的宏_MY_OPT_就无法匹配到而引发错误。#error指令会将后面的语句输出,从而提醒用户要使用这个头文件,这样一来,通过预处理时的断言,发布者就可以避免一些头文件的引用问题。

        类似的,还有#warning 这种用于生成编译警告消息预处理宏。

二、编译断言

        在C++11标准中,引入了 static_assert断言来解决这个问题。static_assert使用起来非常简单,它接收两个参数,一个是断言表达式,这个表达式通常需要返回一个bool值;一个则是警告信息,它通常也就是一段字符串。该关键字的用法类似C++11标准之前开源库 Boost内置的BOOST_STATIC_ASSERT断言机制类似,利用1/(e)这个表达式来判定

#define assert_static(e) \
	do{ \
		enum { assert_static = 1/(e) }; \ 
	)while(0)

        在通常情况下,static_assert可以用于任何名字空间,在预处理阶段,static_assert 宏会被展开成名为 _Static_assert 的 C 关键字。该关键字以类似“函数调用”的形式在 C 代码中使用,它的第一个参数接收一个常量表达式。程序在被编译时,编译器会对该表达式进行求值,并将所得结果与数字 0 进行比较。若两者相等,则程序终止编译,并会将通过第二个参数指定的错误信息,与断言失败信息合并输出。若两者不相等,程序会被正常编译,且该关键字对应的 C 代码不会生成任何对应的机器指令。

int bit_deal (int& a) ( 
	static_assert(sizeof(int) >=4,"the parameters of bit should have at least 4 width.");
);

        该程序限定程序在被编译时,其所在平台上 int 类型的宽度需要大于等于 4 字节,编译会被终止,对应的错误信息也会被打印出来:
        error: static assertion failed: "the parameters of bit should have at least 4 width."
        这样的错误信息就非常清楚,也非常有利于程序员排错。一般来说,我们会在程序运行前使用静态断言,来检查它所需要满足的一系列要求。如上面例子,通过静态断言,开发者便可以提前得知,程序如果运行在当前平台上,是否能正常工作。

        类似的用例还有很多,比如判断 char 类型的默认符号性,或是判断指针类型与 int 类型的宽度是否相等,或是判断某个结构体的大小是否满足预期要求,等等。这些都是可能影响 c/c++ 程序运行正确性的因素,而通过静态断言,它们都可以在编译时被提前检测出来。

        通常建议,static_assert写在函数体外通常是较好的选择,这让代码阅读者可以较容易发 现static_assert为断言而非用户定义的函数。

        另外必须注意的是,static_assert的断言表达式的结果必须是在编译时期可以计算的表达式,即必须是常量表达式。如果读者使用了变量,则会导致错误,如:

int bit_deal (int a) ( 
	static_assert( a>=1,"the parameters of a should >=1.");
);

        上面使用了参数变量a ,因而static_assert无法通过编 译。如果需要对变量进行检查,就要是实现运行时的检查,使用assert宏了。

三、运行断言

        在C++中,标准在 <cassert>或vassert.h>头文件中为程序员提供了 assert宏,用于在运行时进行断言。前面错误示例改成assert宏实现就正确了。与静态断言使用的 static_assert 不同,assert 并不支持自定义错误消息。

int bit_deal (int a) ( 
	assert(a>=1);
);

        需要注意的是,C 程序中的运行时断言是否可用,也会受到宏常量 NDEBUG 的影响。当该宏常量的定义先于 #include <assert.h> 语句出现时,编译器会忽略对 assert 宏函数调用代码的编译。反之,它便会在程序运行时进行正常的断言检查。通过这种方式,我们可以相对灵活地控制运行时断言的启用与关闭。事实上,assert宏在<cassert>中的实现方式类似:

#ifdef NDEBUG
#define assert(expr)    (static_cast<void>(0))
#else
//其他
#endif

        类似地,我们也可以借助标准中忽略对 assert 宏函数调用代码的编译的做法,实现我们自定定义的一些在不同编译条件下的代码实现

#ifdef NDEBUG
#define LOG(...) (static_cast<void>(0))
#else
#define LOG(...) { \
	printf("%s: Line *d:\t", __FILE__, __LINE__); \
	printf(__VA_ARGS__); \
	printf("\n"); \
}
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

py_free-物联智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值