核心内容摘要
STM32 USART TC标志位原理与RS-485方向控制实战
文章目录
assert 是什么
基本语法和使用步骤
1 核心语法
2 最简单的示例
assert 的工作机制
1 如何定义 NDEBUG关闭断言
assert 的适用场景
assert 的使用禁忌禁忌1断言中包含有副作用的表达式禁忌2用断言检查生产环境需要处理的错误禁忌3在断言中检查用户输入的合法性禁忌4频繁使用断言检查无关紧要的小条件
assert 与 if 条件判断的区别
自定义断言
总结
assert 是什么assert是C语言的断言宏定义在头文件assert.h中核心作用是在程序运行时检查某个条件是否为真如果条件为真非0程序继续正常执行如果条件为假0程序会立即终止并在标准错误流stderr中打印断言失败的信息包括文件名、行号、失败的条件表达式方便开发者快速定位bug。
简单说assert是程序员给程序加的“运行时检查点”用于验证程序运行过程中必须满足的前提条件是调试阶段的重要工具。
基本语法和使用步骤
1 核心语法#includeassert.h// 必须包含头文件assert(条件表达式);条件表达式可以是任意能产生布尔值0/非0的表达式如变量判断、逻辑运算、函数返回值等无返回值本质是一个宏不是函数编译时会被预处理展开。
2 最简单的示例#includestdio.h#includeassert.hintmain(){inta10;// 断言a的值必须等于10assert(a
;printf(断言成功程序继续执行\n);a5;// 断言a的值必须等于10此时条件为假断言失败assert(a
;printf(这行代码不会执行\n);return0;}运行结果断言失败Assertion failed:(a
,function main,file test.c,line
Abort trap:6// 程序异常终止不同系统提示可能略有不同如Windows的“程序停止运行”从结果能直接看到失败的条件、所在函数、文件名、行号快速定位问题位置。
assert 的工作机制assert是带调试开关的宏其行为受预编译宏NDEBUGNo Debug无调试控制未定义NDEBUG时默认状态调试模式assert(条件)会被展开为完整的检查逻辑包含条件判断、错误信息打印、程序终止调用abort()函数。
定义NDEBUG时发布模式assert(条件)会被直接展开为空语句编译器会把所有断言代码全部剔除不会产生任何运行时开销。
1 如何定义 NDEBUG关闭断言有两种常用方式效果完全一致方式1代码中预处理阶段定义必须放在assert.h之前#defineNDEBUG// 关闭所有assert#includeassert.h方式2编译时通过编译器参数定义推荐无需修改代码GCC/Clang 编译器gcc test.c -otest-DNDEBUG# -D 表示定义宏VS 编译器cl test.c /DNDEBUG
assert 的适用场景断言的核心原则检查“程序运行时必须满足的、不满足则程序逻辑必然错误的前提条件”仅用于调试阶段常见适用场景检查函数入参的合法性尤其是对外暴露的接口/核心函数比如函数要求入参不能为NULL、不能为负数用断言快速排查非法入参// 求整数的绝对值入参无要求但内部函数要求n0intabs(intn){intinner_abs(intx){assert(x
;// 内部函数前提x非负returnx;}returnn0?inner_abs(n):inner_abs(-n);}检查数组/指针的边界合法性比如数组索引不能越界、指针不能为NULLintarr[5]{1,2,3,4,5};intidx6;assert(idx0idx
;// 断言索引在合法范围printf(%d\n,arr[idx]);// 若断言失败避免数组越界访问检查程序逻辑的“不可能状态”比如switch-case没有default时断言永远不会执行到某行intnum3;switch(num){
:printf(1\n);break;
:printf(2\n);break;default:assert(
;// 断言不会执行到这里若执行则num非法}检查函数的返回值必须成功的调用比如内存分配、文件打开这类“调试阶段必须成功失败则环境有问题”的操作FILE*fpfopen(test.txt,r);assert(fp!NULL);// 调试阶段文件必须能打开失败则检查文件路径/权限
assert 的使用禁忌断言是调试工具绝对不能用于生产环境的业务逻辑检查以下场景严禁使用assert否则会导致发布版本程序出错禁忌1断言中包含有副作用的表达式比如自增i、自减j--、函数调用func()、赋值a1等因为关闭断言后这些表达式会被直接剔除导致程序逻辑改变。
❌ 错误示例inti0;assert(i
;// 有副作用i自增调试模式下i会自增发布模式下i被剔除i始终为0程序逻辑完全混乱。
✅ 正确做法把有副作用的表达式抽离出来再断言inti0;i;assert(i
;// 无副作用安全禁忌2用断言检查生产环境需要处理的错误比如文件打开失败、内存分配失败、网络连接失败等这些错误在生产环境中是可能发生的需要用if做实际的错误处理而不是断言。
❌ 错误示例生产环境会出问题FILE*fpfopen(test.txt,r);assert(fp!NULL);// 生产环境关闭断言后fp为NULL也会继续执行后续操作崩溃✅ 正确做法调试用断言生产用if处理FILE*fpfopen(test.txt,r);assert(fp!NULL);// 调试阶段快速排查问题if(fpNULL){// 生产环境实际处理错误perror(fopen failed);// 打印错误信息return-1;// 优雅退出避免崩溃}禁忌3在断言中检查用户输入的合法性用户输入是不可控的比如用户输入负数、空字符串属于业务逻辑范畴必须用if判断并给出友好提示不能用断言直接终止程序。
禁忌4频繁使用断言检查无关紧要的小条件断言是“关键检查点”如果到处加断言会导致代码臃肿也会让开发者忽略真正重要的断言失败。
assert 与 if 条件判断的区别很多新手会混淆assert和if核心区别在于使用目的和适用阶段一张表讲清特性assert 断言if 条件判断核心目的调试阶段快速定位bug生产环境处理业务逻辑/错误条件不满足的行为程序立即终止打印错误信息执行分支逻辑容错/提示运行时开销调试模式有发布模式无调试/发布模式都有适用条件必须满足的前提条件可能发生的普通条件表达式要求无副作用可包含副作用
自定义断言如果默认的断言信息不够详细或者需要自定义断言失败的行为比如打印自定义日志、释放资源可以基于NDEBUG实现自己的断言宏示例#includestdio.h#includestdlib.h// 包含abort()// 自定义断言宏MY_ASSERT#ifndefNDEBUG#defineMY_ASSERT(cond)\do{\if(!(cond)){\fprintf(stderr,【自定义断言失败】%s:%d: 条件(%s)不成立\n,__FILE__,__LINE__,#cond);\// 这里可以加自定义逻辑释放资源、记录日志等 \ abort(); // 终止程序 \ } \ } while(
#else#defineMY_ASSERT(cond)// 发布模式为空#endifintmain(){intpNULL;MY_ASSERT(p!NULL);// 调用自定义断言return0;}运行结果【自定义断言失败】test.c:18:条件(p!NULL)不成立 Abort trap:6其中__FILE__预定义宏代表当前源文件名字符串__LINE__预定义宏代表当前行号整数#cond宏的字符串化操作把条件表达式转为字符串do-while(
保证宏在任何语法场景下都能正确执行比如if后不加{}。
总结assert是assert.h中的断言宏用于调试阶段检查必须满足的前提条件失败则终止程序并打印定位信息成功则继续执行受NDEBUG宏控制定义后断言会被完全剔除无运行时开销适合调试/发布模式切换核心用法检查函数入参、指针/数组边界、程序逻辑的不可能状态仅用于调试三大禁忌不包含副作用表达式、不替代生产环境的错误处理、不检查用户输入与if的本质区别assert是调试工具终止程序if是生产逻辑容错处理。
核心口诀断言查 “必真条件”调试用if处理 “可能错误”生产用。