最近才发现,原来assert这么好用啊。。。
再看看是怎么实现的,又找到了些有趣的东西。
用法:
先包含
#inlcude <assert.h>
在想用的地方给一句:
assert(expression)就可以了。
expression是任意有效的逻辑表达式。
比如:
FILE *fp = fopen("in.txt","r") ;
if ( ! fp ){
exit(0) ;
}
assert(fp != NULL) ;
当expression不满足时,就会报出一个很丑陋的框框,
然后向控制台输出assert不满足的文件和行号。
具体到debug的时候,
可以撒网式地在各个地方放上认为应该为真的表达式的assert,
说不定哪个就爆了,于是趁机发现了问题。
原理:
只要有源码就没有秘密,
所以打开assert.h,看看里面是怎么写的。
主要的就这两句:
_CRTIMP void __cdecl _assert(void *, void *, unsigned);
#define assert(exp) (void)( (exp) || (_assert(#exp, __FILE__, __LINE__), 0) )
第一句就干的就是输出一些信息,然后弹出个框框,
顺便结束程序这些勾当。
他被调用的时候,是类似于:
_assert("false" , "c:\\1.cpp" , 15)
这样。
第二句的构造可谓精简啊,小小一句话还包含了挺多以前没注意到的事情。
1.短路求值
这个是c的重要特性,在处理&&的时候前面为假则不用继续,
在处理|| 的时候,前面为真则不用继续。
形象地说把后面的表达式短路了。
2.单行宏
#exp 生成"exp"这样的字符串
#@a 生成'a'这样的字符
a##b 把a和b连接起来
第一个用法在这里见到了,第二个暂时还没见到用的实例。
第三个在a和b是宏的参数的时候有用。否则直接的ab会被当作一个东西。
3.特殊的预定义宏
__FILE__ 会被替换成所在的文件,字符串形式
__LINE__ 会被替换成行号,unsigned类型
__DATE__ 会被替换成日期
__TIME__ 会被替换成时间
其实之前翻过的跟C有关的书应该都讲了这些的。
不过拿着一个列表,又不给出真正实用的例子,
当然不知道这些东西是怎么回事,
久了自然也就忘了。
4.逗号表达式
感觉实在是一个用的很少的事情,
毕竟有多句话的时候,完全可以用分号就行了。
虽然有好多地方在if之类的里面很压缩的用逗号表达式写好几句话,
其实都可以改得不用逗号表达式的。
其一是逗号表达式的优先级很低,所以后面那对括号实在是不可缺少。
其二是逗号表达式的值为最右边式子的值。
这个估计很多人都记过,但不见得有啥重大意义。
这里,倒确实是发挥了他的意义。
因为_asert这个函数是void型的,
如果不使用逗号表达式在右边补个0的话,
会报告:(VC6)
error C2297: '||' : illegal, right operand has type 'void'
改编:
知道是怎么回事,当然可以很容易做出自己想要的东西。
再说还有asert.h里面的参照呢。
比如,我嫌默认的_assert弹出的东西看着太压抑了。。。
就自己写个就行了。
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_BUFFER 200
void _assert(char *msg , char *file , unsigned line){
char buf[MAX_BUFFER] ;
sprintf(buf , "assertion fail:\n%s\nin file:\n%s\non line:\n%d" , msg , file , line) ;
::MessageBox(NULL , buf , "assertion failure" , MB_OK) ;
exit(0) ;
}
#define assert(exp) ((exp) || (_assert(#exp , __FILE__ , __LINE__) , 0) )
int main(){
assert(1 == 1 && 3 == 4) ;
return 0 ;
}
效果:
至于我的这个是不是更压抑。。那不属于这里讨论的问题了。
反正通过简单变更_assert函数,可以把相关情况输出到文件,
或者选择另外的方式表达出来,能想到的都可以。
至于assert这个宏,也有可以动手脚的地方。
自带的是assert一个为真的表达式。
有的时候就想assert一个为假的表达式,当他为真的时候发出警告。
比如:
FILE *fp = fopen("in.txt","r") ;
if ( ! fp ){
exit(0) ;
}
warn(fp == NULL)
套用上面的写法,既然是为真发警告,那么用&&去换||就行了。