原创
vc断言
2010-8-20 12:11
3240
14
14
分类:
软件与OS
一、关于断言
所谓的断言就是可以肯定为正确的一个陈述语句。
假设某个函数需要一个指向文档对象的指针作为参数,但却错误地使用了一个视图指针来调用该函数。如果函数继续使用该错误地址,轻则程序运行得不到正确的结果,重则破坏视图数据。由于这种错误往往要到使用视图数据时才会发现,因而要找出导致错误的根本原因就要付出相当大的代价了。
只要在函数开始部分加入断言检查,检验指针是否真正指向一个文档对象,就可以完全地避免此类问题的产生。
二、ASSERT宏
ASSERT宏能够计算作为参数传递的表达式值。如果表达式为真,则执行继续。否则,程序显示一个消息并中断。此时可以选择忽略错误、终止程序或进入调试器。下面是如何在函数中应用ASSERT宏验证参数的一个例子:
void foo(char* p,int size)
{
ASSERT(p != 0); // 验证缓冲区指针
ASSERT((size >= 100); // 确认缓冲区大小至少为100字节
// foo 函数的其它计算过程
}
如果没有定义_DEBUG预处理符,则该语句不会真正生成代码。Visual C++会在调试模式编译时自动定义_DEBUG,而在发行模式下,该预处理符是不存在的。如果定义了_DEBUG,则上述两个断言生成的代码类如:
//ASSERT(p != 0);
do
{
if(!(p != 0) && AfxAssertFailedLine(__FILE__, __LINE__))
AfxDebugBreak();
} while(0);
//ASSERT((size >= 100);
do
{
if(!(size >= 100) && AfxAssertFailedLine(__FILE__,__LINE__))
AfxDebugBreak();
}while(0);
do-while结构在一个单独的语句块之内封装了整个断言操作。if语句计算断言表达式,如果值为0则调用AfxAssertFailedLine()。AfxAssertFailedLine()显示消息框并提供 “Abort,Retry,or Ignore”选择,如果选择Retry则调用AfxDebugBreak(),并由此激活调试器。
和AfxAssertFailedLine()的简单目的(即显示对话框以供选择)相比,它的实现显得非常复杂。该函数的源代码在afxasert.cpp内,可以发现它使用了一些特殊的函数以确保消息框正确显示。举个例子,如果断言失败是在程序发送WM_QUIT消息之后的某个位置,则AfxAssertFailedLine()为了显示消息框必须临时地从队列中删除这个消息。
传递给AfxAssertFailedLine()的参数__FILE__ 和 __LINE__分别为代表程序文件名和当前行号的预处理符号。它们由ANSI标准定义,由编译器自动生成具体数值。
三、VERIFY宏
由于ASSERT仅在程序的调试版起作用,测试表达式总是被动的。也就是说,它们不能包含赋值、增量、减量等真正改变数据的操作。但有时候我们需要验证一个主动表达式,比如赋值语句。这时可以使用VERIFY代替ASSERT。下面是一个例子:
void foo(char* p,int size)
{
char* q; // 指针的副本
VERIFY(q = p); // 拷贝指针并执行验证
ASSERT((size >= 100); // 确保缓冲区大小至少为100字节
// 执行 foo 的其它操作
}
在调试模式下ASSERT和VERIFY是相同的。但在发行模式下,VERIFY能够继续对表达式求值(但不再进行断言检验),而ASSERT语句在效果上就如同已经删除了一样。
尽管在MFC源代码中可以找到一些应用VERIFY的例子,但ASSERT用得更为普遍。一些程序员总是完全避免使用VERIFY,因为他们已经习惯于使用被动断言。请记住,如果在ASSERT语句中使用了主动表达式,编译器不会发出任何警告。在发行模式下编译时该表达式会被直接删除,从而导致程序运行的错误。由于发行版程序不含调试信息,这种类型的错误是很难找到原因的。
文章评论(0条评论)
登录后参与讨论