3
利用断言进行补救
说老实话 memcpy中的调试码编得非常蹩脚,且颇有点喧宾夺主的意味。
void memcpy(void* pvTo, void* pvFrom, size_t size)
{
void* pbTo = (byte*)pvTo;
void* pbFrom = (byte*)pvFrom;
assert(pvTo != NULL && pvFrom != NULL);
while(size-->0)
*pbTo++ == *pbFrom++;
return(pvTo);
}
既要维护程序的交付版本又要维护程序的调试版本
aasert
是个只有定义了 DEBUG 才起作用的宏,如果其参数的计算结果为假,就中止调用程序的执行。因此在上面的程序中任何一个指针为 NULL都会引发 assert。
assert
并不是一个仓促拼凑起来的宏,为了不在程序的交付版本和调试版本之间引起重
要的差别,需要对其进行仔细的定义。宏 assert不应该弄乱内存,不应该对未初始化的数
据进行初始化,即它不应该产主其他的副作用。正是因为要求程序的调试版本和交付版本行
为完全相同,所以才不把 assert作为函数,而把它作为宏。如果把 assert
作为函数的话,
其调用就会引起不期望的内存或代码的兑换。要记住,使用 assert的程序员是把它看成一
个在任何系统状态下都可以安全使用的无害检测手段。
读者还要意识到,一旦程序员学会了使用断言,就常常会对宏 assert进行重定义。例如,程序员可以把 assert
定义成当发生错误时不是中止调用程序的执行,而是在发生错误
的位置转入调试程序。assert的某些版本甚至还可以允许用户选择让程序继续运行,就仿佛
从来没有发生过错误一样。
下面是一种用户自己定义宏 ASSERT的方法
#ifdef DEBUG
void _Assert(char* , unsigned); /* 原型 */
#define ASSERT(f) \
if(f) \
NULL; \
else \
_Assert(__FILE__ , __LINE__)
#else
#define ASSERT(f) NULL
#endif
void _Assert(char* strFile, unsigned uLine)
{
fflush(stdout);
fprintf(stderr, \nAssertion failed: %s, line %u\n ,strFile, uLine);
fflush(stderr);
abort();
}
现在如果用 NULL指针调用 memcpy, ASSERT就会抓住这个错误,并显示出如下的
错误信息:
Assertion failed: string.c , line 153
这给出了 assert与 ASSERT
之间的另一点不同。
不管断言宏最终是用什么样方法定义的,都要使用它来对传递给相应函数的参数进行确
认。如果在函数的每个调用点都对其参数进行检查,错误很快就会被发现。断言宏的最好作
用是使用户在错误发生时,就可以自动地把它们检查出来。
要使用断言对函数参数进行确认
转载请注明原创连接:http://blog.csdn.net/wujunokay/article/details/17723381