C语言之不常用的预编译命令
最近在做单片机的开发,我发现一些功能需要反复使用(个人习惯),所以今天决定写一些常用的函数作为自己的库。然后发现了一个自己忽略了很久的问题,那就是怎么合理使用预编译命令来实现接口的保留。
我个人在单片机开发有个小习惯就是上电先闪一下LED灯,说明系统正常上电了,同时也是对芯片的一个简单检查吧。然后有以下两个文件:led.h和led.c
#ifndef _USER_LED_H
#define _USER_LED_H
#include "stm32f1xx_hal.h"
#include "main.h"
// 拉低点亮
#define LIGHT_UP_LOW
#ifdef LIGHT_UP_LOW
#define GPIO_PIN_LIGHT_UP GPIO_PIN_RESET
#define GPIO_PIN_LIGHT_DOWN GPIO_PIN_SET
#else
#define GPIO_PIN_LIGHT_UP GPIO_PIN_SET
#define GPIO_PIN_LIGHT_DOWN GPIO_PIN_RESET
#endif
#define LED0_LIGHTUP HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_LIGHT_UP) // 点亮LED0
#define LED0_LIGHTDOWN HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_LIGHT_DOWN) // 熄灭LED0
#define LED0_TOGGLE HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin) // 切换状态
#define LED1_LIGHTUP HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_LIGHT_UP)
#define LED1_LIGHTDOWN HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_LIGHT_DOWN)
#define LED1_TOGGLE HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin)
void initLed(void);
#endif
#include "USER/LED/include/led.h"
#include "USER/tool/include/tool.h"
/**
* @brief
* 板子上的三个led灯点亮一秒后关闭
*/
void initLed(void){
LED0_LIGHTUP;
LED1_LIGHTUP;
SLEEP(1000);
LED0_LIGHTDOWN;
LED1_LIGHTDOWN;
}
在这里,用到了最基础的也是大家最常用的预编译命令:
- #include
- #define
- #ifndef
- #ifdef
- #else
- #endif
这些命令来说都是我们比较熟悉的,也足够应对大多数的情况,但我遇到了一个不好处理的情况:由于板子没有显示屏,实时数据不方便,我就日常使用串口将数据发送到Pc,通过出串口助手进行数据的检查,但是,也不是所有的板子都是有串口芯片的,有的是通过USB进行模拟,也就是stm32的usb虚拟串口来实现串口发送的功能。
由于在使用时,可能不是同样的芯片,并且可能是直接使用USART进行数据发送,也可能是USB进行模拟发送,所以我打算于预留这个接口,但时候用的时候再来实现。
于是,我在tool.h文件中,定义了函数 sendDataToPc,在源文件中进行了函数体的预留。
#ifndef _USER_TOOL_H
#define _USER_TOOL_H
#define SLEEP(n) HAL_Delay(n)
#define SEND_MSG(msg) sendDataToPc((unsigned char *)msg, strlen(msg))
void sendDataToPc(unsigned char *data, int len);
void sendDigit(char *name, long long num);
#endif
#include "USER/tool/include/tool.h"
#include <stdio.h>
#include <string.h>
#define _UNDEFINE_SEND_MSG_TO_PC 1
void sendDataToPc(unsigned char *data, int len)
{
// 如果为定义这个函数,则报错,定义后修改宏 _UNDEFINE_SEND_MSG_TO_PC 为0
#if _UNDEFINE_SEND_MSG_TO_PC
#warning "The function 'sendDataToPc' need to define"
#endif
return;
}
void sendDigit(char *name, long long num)
{
if (name == NULL)
{
return;
}
char msg[256] = {0};
sprintf(msg, "Parameter\"%s\": %d / %X", name, num, num);
SEND_MSG(msg);
}
为了不影响编译时这里的一个错误导致其他地方报了N个错误,又不希望到时候导入后忘了这里没有实现,我使用了警告。
同时呢,我设置了宏 _UNDEFINE_SEND_MSG_TO_PC 为1,在实现时就可以选择实现后修改这个宏来取消这个warning或者是注释掉这一段的代码。通过预编译命令 #if 和 #warning的组合来达到既不影响其他地方引用是编译报错,又提醒自己这里还有个函数没有现实的目的。
在代码中,我们可能不会经常用到#warnign 和 #error,比起其他的预编译,我们使用的频率会低很多,但如果是接口设计,个人感觉在需要的位置做好处理,对于后期他人的使用和代码调试,意义重大。
但同样来说,用不好也是挺头大的。我想大家都遇到过编译后error 3个,然后修改完最后一个以为可以完事的时候,error 90个的这种情况,比如某个宏未定义,然后在几个文件中被用到就会有一大堆的error,看着都心烦。
从我个人经验来看,我们应该尽量少的去使用error,而是多使用warning,并在设计时候,注意接口的耦合性,要尽可能的解耦。比如我上述的代码中,其实串口发不出数据,并不会影响我板子控制设备。比如我的功能是控制电机正转10圈,如果我编译后电机正常工作,那么这里的warning自然可以不用理会。而如果出现了问题,我需要输出信息的时候,再去实现这个函数也是可以的。
很多人可能认为warning其实是可以不用理会的,但我觉得还是应该稍微的重视一下warning,他可能在现在没有对你的程序造成影响,但未必以后不会,我们没法保证自己的测试就测完了所有的可能性,可能在某些情况下这里的warning会是致命。
除了 #warning 和 #error,我还想说一下 #pragma这个预编译命令。
预编译命令 #pragma 是功能最多的一个预编译命令了,但很多人甚至从来没有使用过。这个命令在不同的编译器下,功能也是不一样的,比如我们想在编译时输出一个信息:
#pragma message("消息文本")
虽然我们有error和warning,但是不是这样更加合理呢?但同样,这个编译命令可能在一些编译器下就会无效。
使用VS的使用,我其实常用 #pragma once, 这个命令是说这个文件只会导入一次,优点是比使用 #ifndef + #define + #endif 要简单,缺点就是如果这个命令不生效,我们还是需要去用后者来避免头文件的多次导入。
除此之外,如果使用VS开发,有些人可能就会遇到 warning 4996,虽然是warning,但是确实不能运行,其中一个方法就是在文件中使用 #pragma warning(disable:4996) 来指定关闭一个警告,这也说明了这个命令的强大。但是,我更倾向于于直接去VS的项目属性中禁用这个警告,毕竟更加的操作简单,而且一劳永逸,同时也证明这个命令其实是有点鸡肋的。
标签:LED0,常用,PIN,C语言,编译,warning,GPIO,define From: https://www.cnblogs.com/joke-dream/p/16790704.html