NDEBUG ,DEBUG 宏 和 assert() 的用法

2022年 5月 20日 82点热度 0人点赞

引言

NDEBUG 和 DEBUG 宏的用法

NDEBUG 宏是 Standard C 中定义的宏, 专门用来控制 assert() 的行为. 如果定义了这个宏, 则 assert 不会起作用.

#ifdef NDEBUG
#define assert(x) ((void)0)
#else
...

C Standard 中规定了 assert 以宏来实现. <assert.h> 被设计来可以被多次包含, 其中一上来就 undef assert, 然后由 NDEBUG 宏来决定其行为. 如:

#ifdef NDEBUG
    #undef NDEBUG
    #include <assert.h>
    #define NDEBUG
#else
    #include <assert.h>
#endif // #ifdef NDEBUG

将以上代码放在 c/cpp 最上方 (当然, 其后不能出现其它 #include <assert.h> 语句), 就可以保证 c/cpp 所在的编译单元中的 assert, 在 release 下也会触发 abort 行为.

NDEBUG 宏由于字面意思, 也被用于作为判断 debug/release 版本的宏, 不过这个是编译器, 环境相关的, 并不可靠.

比如 vc 中, 对生成的 release 版本项目, 默认会定义这个宏, 而 gcc 并没有定义, 得用 -DNDEBUG 参数来定义.

C++ Standard 中完全采用了 C 中的定义.

assert() 函数用法

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

#include <assert.h>
void assert(int expression);

assert 的作用是现计算表达式 expression, 如果其值为假 (即为 0), 那么它先向 stderr 打印一条出错信息, 然后通过调用 abort 来终止程序运行.

请看下面的程序清单 badptr.c:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
int main(void)
{
    FILE *fp;
    fp = fopen("test.txt", "w"); //以可写的方式打开一个文件, 如果不存在就创建一个同名文件
    assert(fp);                  //所以这里不会出错
    fclose(fp);

    fp = fopen("noexitfile.txt", "r"); //以只读的方式打开一个文件, 如果不存在就打开文件失败
    assert(fp);                        //所以这里出错
    fclose(fp);                        //程序永远都执行不到这里来
    return 0;
}

测试

[root@localhost  error_process]#  gcc  badptr.c
[root@localhost  error_process]#  ./a.out
a.out:  badptr.c:14:  main:  Assertion  `fp'  failed.

使用 assert 的缺点是, 频繁的调用会极大的影响程序的性能, 增加额外的开销.
在调试结束后, 可以通过在包含 #include <assert.h> 的语句之前插入 #define NDEBUG 来禁用 assert 调用, 示例代码如下:

#include <stdio.h>
#define NDEBUG
#include <assert.h>

用法总结与注意事项:

1) 在函数开始处检验传入参数的合法性

如:

int resetBufferSize(int nNewSize)
{
    //功能: 改变缓冲区大小,
    //参数:nNewSize  缓冲区新长度
    //返回值: 缓冲区当前长度
    //说明: 保持原信息内容不变          nNewSize<=0 表示清除缓冲区
    assert(nNewSize >= 0);
    assert(nNewSize <= MAX_BUFFER_SIZE);
    ...
}

2) 每个 assert 只检验一个条件, 因为同时检验多个条件时, 如果断言失败, 无法直观的判断是哪个条件失败

不好:

assert(nOffset >= 0 && nOffset + nSize <= m_nInfomationSize);

好:

assert(nOffset >= 0);
assert(nOffset + nSize <= m_nInfomationSize);

3) 不能使用改变环境的语句, 因为 assert 只在 DEBUG 生效, 如果这么做, 会使用程序在真正运行时遇到问题

错误:

assert(i++ < 100)

这是因为如果出错, 比如在执行之前 i=100, 那么这条语句就不会执行, 那么 i++ 这条命令就没有执行.

正确:

assert(i < 100);
i++;

4) assert 和后面的语句应空一行, 以形成逻辑和视觉上的一致感

5) 有的地方, assert 不能代替条件过滤

rainbow

这个人很懒,什么都没留下

文章评论