断言和注释规范
大纲
- 断言
- Doxygen 注释规范
- 防止头文件包含重复
具体案例
断言
先看示例代码:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
/* ------- 以下内容省略,跟前面我们定义的函数内容相同----- */
基本上每个库函数的开头都会有这样类似的内容,这里的“assert_param”实际是一个宏,在库函数中它用于检查输入参数是否符合要求,若不符合要求则执行某个函数输出警告
“assert_param”的定义
#ifdef USE_FULL_ASSERT
/**
* @brief assert_param 宏用于函数的输入参数检查
* @param expr: 若 expr 值为假,则调用 assert_failed 函数
* 报告文件名及错误行号
* 若 expr 值为真,则不执行操作
*/
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)_
,→_FILE__, __LINE__))
/* 错误输出函数 ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0)
#endif
这段代码的意思是,假如我们不定义“USE_FULL_ASSERT”宏,那么“assert_param”就是一个空的宏 (#else 与 #endif 之间的语句生效),没有任何操作。从而所有库函数中的 assert_param 实际
上都无意义,我们就当看不见好了。
假如我们定义了“USE_FULL_ASSERT”宏,那么“assert_param”就是一个有操作的语句 (#if 与#else 之间的语句生效),该宏对参数 expr 使用 C 语言中的问号表达式进行判断,若 expr 值为
真,则无操作 (void 0),若表达式的值为假,则调用“assert_failed”函数,且该函数的输入参数为“FILE”及“LINE”,这两个参数分别代表“assert_param”宏被调用时所在的“文件名”及“行号”。
但库文件只对“assert_failed”写了函数声明,没有写函数定义,实际用时需要用户来定义,我们一般会用 printf 函数来输出这些信息
void assert_failed(uint8_t * file, uint32_t line)
{
printf(“\r\n 输入参数错误,错误文件名 =%s, 行号 =%s”,file,line);
}
Doxygen 注释规范
下面还是先一段代码示例
/**
* @brief 初始化控制 LED 的 IO
* @param 无
* @retval 无
*/
这 是 一 种 名 为 “Doxygen” 的 注 释 规 范, 如 果 在 工 程 文 件 中 按 照 这 种 规 范 去 注 释, 可以使用 Doxygen 软件自动根据注释生成帮助文档。我们所说非常重要的库帮助文档《stm32f10x_stdperiph_lib_um.chm》,就是由该软件根据库文件的注释生成的。关于 Doxygen 注释规范本教程不作讲解,感兴趣的读者可自行搜索网络上的资料学习。
防止头文件包含重复
一般来说,我们不会直接在 C 的源文件写两个“#include”来包含同一个头文件,但可能因为头文件内部的包含导致重复,这种代码主要是避免这样的问题。
所以以bsp_led.h为例
“bsp_led.h”文件中使用了“#include“stm32f10x.h””语句,按习惯,可能我们写主程序的时候会在 main 文件写“#include“bsp_led.h”
及 #include“stm32f10x.h””,这个时候“stm32f10x.h”文件就被包含两次了,如果没有这种机制,就会出错。
#ifndef __LED_H
#define __LED_H
/* 此处省略头文件的具体内容 */
#endif /* end of __LED_H */
这里宏定义的名称是自己写的,最好和头文件保持一致
在头文件的开头,使用“#ifndef”关键字,判断标号“__LED_H”是否被定义,若没有被定义,则从“#ifndef”至“#endif”关键字之间的内容都有效,也就是说,这个头文件若被其它文件“#include”,
它就会被包含到其该文件中了,且头文件中紧接着使用“#define”关键字定义上面判断的标号“__LED_H”。当这个头文件被同一个文件第二次“#include”包含的时候,由于有了第一次包含中的“#define __LED_H”定义,这时再判断“#ifndef__LED_H”,判断的结果就是假了,从“#ifndef”至“#endif”之间的内容都无效,从而防止了同一个头文件被包含多次,编译时就不会出现“redefine
(重复定义)”的错误了。