assert_param(expr) 断言

        断言(assert v.)本质上是程序员在代码中写下的 “假设” 或 “承诺”。在程序运行时检查某个条件是否为真。如果条件为真,程序正常执行;如果条件为假,说明程序逻辑出了问题,断言会触发错误内容(比如进入死循环、打印错误信息等)。

一、嵌入式调试中的assert_param() 宏

        断言只用于调试阶段,帮助开发人员快速发现程序中的逻辑错误(比如参数非法、状态异常),让代码更易读(比如assert(p != NULL)说明这里假设指针 p 非空)。

        断言不能用于处理运行时的正常错误(比如文件打不开、网络断开),这些应该用普通的错误处理逻辑比如返回错误码等。下面这段程序如果断言失败会进入死循环。

// 断言测试的引脚掩码 0000 0000 0000 0000 1111 1111 1111 1111
#define GPIO_PIN_MASK         (0x0000FFFFU) 

// 检查传入的__PIN__参数是不是一个合法的 GPIO 引脚值:判断参数低16位含非0,高16位全0,输出真
#define IS_GPIO_PIN(__PIN__)  ((((uint32_t)(__PIN__) & GPIO_PIN_MASK) != 0x00U) &&\
                              (((uint32_t)(__PIN__) & ~GPIO_PIN_MASK) == 0x00U))

// 我断言expr为真,否则进入死循环
#define assert_param(expr) if(!(expr)) while(1); 

// 调用:检查GPIO引脚是否合法
assert_param(IS_GPIO_PIN(GPIO_PIN_5));

        断言核心目的是调试阶段检查参数合法性,发布阶段需要移除检查

//断言空
#define assert_param(expr) if(!(expr)) ((viod)0U)

二、C语言标准库的assert()宏

此外,C 语言标准库<assert.h>提供了assert()宏,这个 assert 宏的作用是:在程序运行时检查 _Expression表达式是否为真。如果为真,宏等价于空操作;如果为假,会调用 _assert 函数(传入断言失败的表达式、文件名、行号),实现断言失败的错误提示 / 程序终止。

//assert.h文件内的宏
#define assert(_Expression) \
 (void) \
 ((!!(_Expression)) || \
  (_assert(#_Expression,__FILE__,__LINE__),0))

#define assert(_Expression) \

(void)((!!(_Expression)) || (_assert(#_Expression,__FILE__,__LINE__),0))

1.    !!(_Expression) 强制转换成布尔值:第一个!把表达式结果取反(非 0→0,0→1),第二个!再次取反,最终把任意值转换成纯布尔值(非 0→1,0→0)。

2.    || 利用短路特性控制是否执行断言失败逻辑:逻辑或 || 的规则是左边为真时,右边直接跳过不执行;左边为假时,才执行右边。

3.    (_assert(#_Expression,__FILE__,__LINE__),0)  断言失败的执行逻辑

        ①逗号运算符,先执行 _assert(#_Expression,__FILE__,__LINE__);

        ②#_Expression:把断言表达式转成字符串(比如 assert(p!=NULL) 传入 "p!=NULL");

        ③__FILE__/__LINE__:告诉 _assert 函数断言失败发生在哪个文件、哪一行;

        ④_assert 是底层实现函数(通常由标准库提供),功能是打印错误信息 + 终止程序(比如调用 abort())。

        ⑤最后返回 0:逗号表达式最终结果为 0,不影响整个 || 表达式的逻辑(此时左边已经是 0,右边返回 0,整体为 0)

4.    (void) 消除编译器警告:整个宏的最终结果被强制转换成 void 类型,告诉编译器 “这个表达式的返回值不需要使用”,避免编译器报 “表达式结果未使用” 的警告。

三、为什么使用||不用if语句判断?

        assert ()宏被设计成可以放在任意表达式位置(比如赋值、逗号表达式中),但 if 是语句,无法嵌入表达式

// 合法场景:断言作为表达式的一部分
int a = 5, b = (assert(a>0), 10); 

// 用if的宏展开后:
int a = 5, b = (if (!(a>0)) {_assert(...);}, 10); 
// 编译器直接报错:if语句不能出现在赋值表达式中!

四、知识点

标准库<assert.h>提供了assert()宏可以使用,HAL库中的assert_param(expr)发布阶段需要((void)0U);

__FILE__:编译器内置宏,代表当前源文件的文件名(字符串);

__LINE__:编译器内置宏,代表当前代码的行号(整数);

#_Expression:宏的 “字符串化” 操作,把传入的表达式转换成字符串(比如 assert(a>0) 会变成 #(a>0) → "a>0");

||:逻辑或运算符,具备短路特性(左边为真时,右边不执行);

(A,B):逗号表达式,执行顺序是先 A 后 B,最终结果为 B 的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值