C 标准库的 assert.h头文件提供了一个名为 assert 的宏,它可用于验证程序做出的假设,并在假设为假时输出诊断消息。
已定义的宏 assert 指向另一个宏 NDEBUG,宏 NDEBUG 不是
#define assert(ignore) ((void)0)
宏里面这样用的目的是防止该宏被用作右值, 因为 void 类型不能用作右值,还有的伙伴可能疑惑,那干嘛不直接用 void,那是因为在编译程序时,首先会预编译程序,把相关的头文件包含进来,并把文件中定义的宏进行替换,直接用 void 的话,预编译时该宏被替换成 void; 这句代码,在编译时编译器会 警告:空声明中类型名无用
库宏
下面列出了头文件 assert.h 中定义的唯一的函数:
| 序号 | 函数&描述 |
|---|---|
| 1 | void assert(int expression) // 这实际上是一个宏,不是一个函数,可用于在 C 程序中添加诊断。 |
| expression:如果 expression 为 非0,assert() 不执行任何动作。如果 expression 为 0,assert() 会在标准错误 stderr 上显示错误消息,并中止程序执行。 |
下面的例子验证:
不能直接用 void 替换 (void)0,预编译程序时宏被替换成 void; 这句代码,在编译时编译器会 警告:空声明中类型名无用
assert_example_user_macro.c 文件
#include
#define user_macro(age) void
int main()
{
int age = 20;
user_macro(age);
printf("age = %d\n", age);
return 0;
}
gcc -o assert_example_user_macro.i -E assert_example_user_macro.c // -E 告诉编译器,对 assert_example_user_macro.c 文件进行预编译,并生成预编译文件 assert_example_user_macro.i
assert_example_user_macro.i 文件截图

gcc -o assert_example_user_macro assert_example_user_macro.c

下面的例子验证:
已定义的宏 assert 指向另一个宏 NDEBUG,宏 NDEBUG 不是的一部分。如果已在引用 的源文件中定义 NDEBUG 为宏名称,则 assert 宏的定义为:#define assert(ignore) ((void)0)
assert_example_release.c 文件
#define NDEBUG 1 // 定义了NDEBUG
#include
#include
#include
char *str_cpy(char *des, const char *src);
int main()
{
char *p = NULL;
char src[] = "Jim is a student.";
p = (char *)malloc(sizeof(src)/sizeof(*src));
str_cpy(p, src);
printf("%s\n", p);
free(p);
return 0;
}
char *str_cpy(char *des, const char *src)
{
assert(des);
assert(src);
char *p = des;
while((*p++ = *src++) != '\0');
return 0;
}
gcc -o assert_example_release.i -E assert_example_release.c // -E 告诉编译器,对 assert_example_release.c 文件进行预编译,并生成预编译文件 assert_example_release.i
assert_example_release.i 文件截图

通过上面的例子也可以证明assert可能被预处理过程被替换成 (void)0,当使用这个宏的时候必须保证条件表达式不存在别的作用。特别的,不应该在assert的条件表达式中使用这些语句:函数调用、对变量赋值、使用修改变量的操作符(如 ++ 等)。不然可能导致一个程序在Release版本下编译出来的可执行文件执行的结果跟Debug版本不一样。
使用例子
assert_example.c 文件
#include
#include
#include
char *str_cpy(char *des, const char *src);
int main()
{
char *p = NULL;
char src[] = "Jim is a student.";
//p = (char *)malloc(sizeof(src)/sizeof(*src));
str_cpy(p, src); // 这里由于传入的 p = NULL,所以assert() 会在标准错误 stderr 上显示错误消息,并中止程序执行。
printf("%s\n", p);
free(p);
return 0;
}
char *str_cpy(char *des, const char *src)
{
assert(des);
assert(src);
char *p = des;
while((*p++ = *src++) != '\0');
return des;
}
gcc -o assert_example.i -E assert_example.c // -E 告诉编译器,对 assert_example.c 文件进行预编译,并生成预编译文件 assert_example.i
assert_example.i 文件截图

可执行文件执行结果
