一文吃透断言 assert

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 优缺点

优点

  1. 快速定位bug(直接给出行号+条件)

  2. 语法简单,零学习成本

  3. 发布版可一键关闭,不影响性能

  4. 能提前暴露隐藏的逻辑错误

缺点

  1. 失败会直接终止程序,不能用于业务错误处理

  2. 生产环境失效,无法用于线上监控

五、重要注意事项

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

六、总结(速记版)

  1. assert是调试宏,需要 #include <assert.h>

  2. 用法:assert(条件),条件为假则程序终止并打印错误

  3. 定义 NDEBUG 后,所有 assert 自动失效(发布版必备)

  4. 用途:检查内部逻辑错误,不处理用户输入/业务错误

  5. 禁忌:不要在 assert 里写带副作用的代码(如 i++

总结

  1. assert 是调试宏,用于检查程序内部必须成立的条件,条件不成立则终止并打印错误位置

  2. 调试模式生效,定义 NDEBUG 后发布版自动失效,无性能损耗

  3. 仅用于排查程序员逻辑错误,不处理用户输入等可预见错误

相关推荐
Digital_Sunrise3 小时前
首发!检测你是否被中转站注入提示词攻击!
后端
fliter3 小时前
Rust 中的小字符串:smol_str 与 smartstring 的对决
后端
一个做软件开发的牛马3 小时前
Java 常用类:String不可变、新时间API与包装类陷阱
java·后端
刀法如飞3 小时前
AI时代:一文搞懂DDD领域驱动设计
后端·架构·ai编程
weixin_468466853 小时前
Prometheus监控服务部署与实战指南
服务器·后端·python·docker·自动化·prometheus
会编程的土豆4 小时前
Go interface 底层的 itab 到底是什么
开发语言·后端·golang
candyTong4 小时前
Claude Code 每次调用 API 时,上下文是怎么"拼"出来的?
javascript·后端·架构
java_cj4 小时前
MySQL 执行原理深度剖析:查询成本计算与优化器内幕
数据库·后端·mysql
java_cj4 小时前
数据库范式化设计与性能优化全攻略
数据库·后端·性能优化·架构·开源