assert 是 C 语言中用于调试程序的宏(不是函数!),核心作用是在程序运行时检查某个条件是否成立:
-
如果条件成立:程序正常继续运行
-
如果条件不成立:程序立即终止,并打印错误信息(包含出错的文件名、行号、条件表达式)
它专门用来排查开发者确定绝对不应该发生的逻辑错误,是调试定位 bug 的神器。
一、基础用法
1. 头文件
必须包含头文件才能使用:
C
#include <assert.h>
2. 语法
C
assert(表达式);
-
表达式:你期望一定为真的条件(非0)
-
如果表达式为假(0),
assert触发断言失败
3. 最简单示例
C
#include <stdio.h>
#include <assert.h>
int main() {
int a = 10;
// 断言:a 必须大于 20(明显不成立)
assert(a > 20);
printf("程序正常运行\n");
return 0;
}
4. 运行结果
程序崩溃,终端输出类似这样的错误:
Plain
assert: test.c:7: main: Assertion 'a > 20' failed.
Aborted (core dumped)
你能直接看到:
-
出错文件:
test.c -
出错行号:第7行
-
失败的条件:
a > 20
二、核心特性(必知)
1. assert 是宏,不是函数
这是最关键的知识点!
-
它的实现是宏定义,不是标准函数
-
行为和函数有区别(比如编译期可关闭)
2. 只在调试模式生效,发布版自动失效
在代码最前面定义 NDEBUG 宏,所有 assert 会被编译器直接忽略,不生成任何代码:
C
// 定义 NDEBUG 后,assert 全部失效
#define NDEBUG
#include <assert.h>
好处:
-
开发调试:用
assert快速查错 -
上线发布:加一行
#define NDEBUG,无性能损耗、无崩溃风险
3. 断言失败会直接终止程序
assert 不会让程序优雅地报错恢复,而是直接退出,所以:
-
绝对不要用它处理用户输入错误(比如用户输了负数)
-
只用来检查内部逻辑错误、程序员疏忽(比如指针为空、数组越界、参数非法)
三、实际开发常用场景
场景1:检查指针不能为空
C
void func(int* ptr) {
// 断言:ptr 绝对不能是 NULL
assert(ptr != NULL);
*ptr = 100;
}
如果传入空指针,直接定位错误,而不是程序莫名其妙崩溃。
场景2:检查函数参数合法性
C
// 年龄必须在 0~150 之间
void set_age(int age) {
assert(age >= 0 && age <= 150);
}
场景3:检查数组/索引不越界
C
int arr[5];
void set_arr(int index, int val) {
assert(index >= 0 && index < 5);
arr[index] = val;
}
四、assert 优缺点
优点
-
快速定位bug(直接给出行号+条件)
-
语法简单,零学习成本
-
发布版可一键关闭,不影响性能
-
能提前暴露隐藏的逻辑错误
缺点
-
失败会直接终止程序,不能用于业务错误处理
-
生产环境失效,无法用于线上监控
五、重要注意事项
1. 不要在 assert 里写业务代码
错误示例:
C
// 危险!关闭 assert 后,i++ 不会执行!
assert(i++ < 10);
正确写法:
C
assert(i < 10);
i++;
2. 不要用它处理用户可预见的错误
比如用户输入负数、文件不存在,应该用 if 判断 + 错误提示,不能用 assert。
3. 先定义 NDEBUG,再包含 assert.h
顺序不能错,否则 assert 不会失效:
C
// 正确
#define NDEBUG
#include <assert.h>
// 错误
#include <assert.h>
#define NDEBUG
六、总结(速记版)
-
assert是调试宏,需要#include <assert.h> -
用法:
assert(条件),条件为假则程序终止并打印错误 -
定义
NDEBUG后,所有assert自动失效(发布版必备) -
用途:检查内部逻辑错误,不处理用户输入/业务错误
-
禁忌:不要在
assert里写带副作用的代码(如i++)
总结
-
assert是调试宏,用于检查程序内部必须成立的条件,条件不成立则终止并打印错误位置 -
调试模式生效,定义
NDEBUG后发布版自动失效,无性能损耗 -
仅用于排查程序员逻辑错误,不处理用户输入等可预见错误