2024年最全嵌入式 C 语言宏配置的各种技巧_嵌入式c语言中的宏(2),物联网嵌入式开发面试真题精选

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

#include <stdint.h>
#include <stdio.h>

static const char *devType = “ABS”;
static uint32_t devID = 34;

void Device_printfMsg(void){
printf(“Device: %s\r\n” , devType);
printf(“DevID: %u\r\n” , devID);
printf(“DomainName: %s_%u.local\r\n” , devType, devID);
}


这样一个简单的设备就完成了:


![图片](https://img-blog.csdnimg.cn/img_convert/468e1aa4cbe5434a922a3790bc5a71ad.png)


但这样实在偶合太严重了。要是现在我多了一台设备,需要多维护一个设备,那最朴实的人肯定就屁颠屁颠的一个个去修改值了。要是偶尔修改一下,而且就几个参数还好,但实际中经常会有多个参数,而且会经常要修改,那直接人工修改就很不靠谱了。


而我第一反应可能会这么搞。


device.c



#include “device.h”
#include <stdint.h>
#include <stdio.h>

#if 0
static const char *devType = “ABS”;
static uint32_t devID = 34;
#else
static const char *devType = “CBA”;
static uint32_t devID = 33435;
#endif

void Device_printfMsg(void){
printf(“Device: %s\r\n” , devType);
printf(“DevID: %u\r\n” , devID);
printf(“DomainName: %s_%u.local\r\n” , devType, devID);
}


这是快速切换技术,这样我只要修改#if后面为1或0就能快速切换不同配置:


![图片](https://img-blog.csdnimg.cn/img_convert/1b1f98edb3c695b159c0b036f7e01b33.png)


观察代码发现,冗余的代码有点多,而且比如那个DomainName,很可能代码其他地方还会经常用到,这样把它的格式放在printf的格式字符串里就很不合适了,我们需要单独为它分配个字符串。于是整理之后就变成了这样。



#include “device.h”
#include <stdint.h>
#include <stdio.h>

#if 0
#define DEV_NAME ABS
#define DEV_ID 34
#else
#define DEV_NAME CBA
#define DEV_ID 33435
#endif

#define _STR(s) #s
#define MollocDefineToStr(mal) _STR(mal)

static const char devType[] = MollocDefineToStr(DEV_NAME);
static uint32_t devID = DEV_ID;
static const char devDName[] = MollocDefineToStr(DEV_NAME) “_” MollocDefineToStr(DEV_ID) “.local”;

void Device_printfMsg(void){
printf(“Device: %s\r\n” , devType);
printf(“DevID: %u\r\n” , devID);
printf(“DomainName: %s\r\n” , devDName);
}


不用看了,运行结果和上面那个一模一样。


#define 就是宏定义,都在看宏配置技巧了应该其实是不需要解释宏在干什么了。但是要强调的是,宏的作用是文本替换,注意是文本,预处理器并不认得变量不变量的,它只知道见到之前定义过的宏,就直接替换文本。


所以:



static uint32_t devID = DEV_ID;


这句其实经过预处理后就是:



static uint32_t devID = 33435;


我们看到其中MollocDefineToStr这个宏很有意思,这对宏是用于把宏展开后的值作为字符串的。


预处理后,



static const char devDName[] = MollocDefineToStr(DEV_NAME) “_” MollocDefineToStr(DEV_ID) “.local”;


这句就会变成:



static const char devDName[] = “CBA” “_” “33435” “.local”;


然后由于C语言里连续的字符串不分割的话会自动合并,上面这就相当于



static const char devDName[] = “CBA_33435.local”;


接下来又来了一台设备。我忍,扩充下快速切换,弄成多路分支的那种。



#include “device.h”
#include <stdint.h>
#include <stdio.h>

#define DEV_ABS 1
#define DEV_CBA 2
#define DEV_LOL 3

// 选择当前的设备
#define DEV_SELECT DEV_LOL

#if (DEV_SELECT == DEV_ABS)
#define DEV_NAME ABS
#define DEV_ID 34
#elif(DEV_SELECT == DEV_CBA)
#define DEV_NAME CBA
#define DEV_ID 33435
#elif(DEV_SELECT == DEV_LOL)
#define DEV_NAME LOL
#define DEV_ID 1234
#else
#error “please select current device by DEV_SELECT”
#endif

#define _STR(s) #s
#define MollocDefineToStr(mal) _STR(mal)

static const char devType[] = MollocDefineToStr(DEV_NAME);
static uint32_t devID = DEV_ID;
static const char devDName[] = MollocDefineToStr(DEV_NAME) “_” MollocDefineToStr(DEV_ID) “.local”;

void Device_printfMsg(void){
printf(“Device: %s\r\n” , devType);
printf(“DevID: %u\r\n” , devID);
printf(“DomainName: %s\r\n” , devDName);
}


这样每次这样在 #define DEV\_SELECT 那修改一下对应的设备就好了,其实可读性还不错。


![图片](https://img-blog.csdnimg.cn/img_convert/071695bbbd7d839de9ab15eadea6357a.png)


那句#error确保了你不会遗忘去配置它,因为如果你配置了个错误的值,预处理器会直接报错。


![图片](https://img-blog.csdnimg.cn/img_convert/6de39b8324074dc34435f5fb24e58639.png)img


这时候,一般来说我会把配置相关的移到头文件中,就变成了这样:


device.h



#ifndef _DEVICE_H
#define _DEVICE_H

#define DEV_ABS 1
#define DEV_CBA 2
#define DEV_LOL 3

#ifndef DEV_SELECT
#define DEV_SELECT DEV_ABS
#endif

#if (DEV_SELECT == DEV_ABS)
#define DEV_NAME ABS
#define DEV_ID 34
#elif(DEV_SELECT == DEV_CBA)
#define DEV_NAME CBA
#define DEV_ID 33435
#elif(DEV_SELECT == DEV_LOL)
#define DEV_NAME LOL
#define DEV_ID 1234
#else
#error “please select current device by DEV_SELECT”
#endif

void Device_printfMsg(void);

#endif


device.c



#include “device.h”
#include <stdint.h>
#include <stdio.h>

#define _STR(s) #s
#define MollocDefineToStr(mal) _STR(mal)

static const char devType[] = MollocDefineToStr(DEV_NAME);
static uint32_t devID = DEV_ID;
static const char devDName[] = MollocDefineToStr(DEV_NAME) “_” MollocDefineToStr(DEV_ID) “.local”;

void Device_printfMsg(void){
printf(“Device: %s\r\n” , devType);
printf(“DevID: %u\r\n” , devID);
printf(“DomainName: %s\r\n” , devDName);
}


这样,这些配置参数就对其他include了这个头文件的文件是可见的了。


至于那句



#ifndef DEV_SELECT
#define DEV_SELECT DEV_ABS
#endif


这句可有个大好处,所有你想要拥有默认参数且想要在不同工程中都可以定制的地方都可以这么写。这样,在编译器选项中定义宏,就可以用同一套源码为不同项目生成项目定制代码。


比如在VS中可以在解决方案资源管理器中的项目条目上右键->属性,打开项目的属性页,在 C/C++ ->预处理器->预处理器定义 中定义宏


![图片](https://img-blog.csdnimg.cn/img_convert/216a66ebbfa1b788f4f0c2279717eaee.png)


CodeWarrior中则是在Edit->Standard Settings里


![图片](https://img-blog.csdnimg.cn/img_convert/c0dd3f5933f0bd874d1876c8d307ce57.png)


当然,有一点点问题就是这样搞没法使用像前面类枚举那种方法来给宏赋值宏,得直接赋值数字、字符串等。


接下来。what!?还要加设备,这样下去不行!一堆#if#else会搞死人的。要是我几十W个设备,难道一个.h文件就几十万行么?我得把配置信息独立出来!


建立一个随便什么名字,甚至随便什么扩展名的文件,扔进工程文件夹,就随便起个名字叫DEVINFO.txt得了。


DEVINFO.txt



// 设备配置信息模板,根据具体设备配置

// 设备名,字符串
#define DEV_NAME DEFAULT
// 设备ID,U32
#define DEV_ID 0


然后修改device模块:


device.h



#ifndef _DEVICE_H
#define _DEVICE_H

#ifndef DEVINFO_FILENAME
#define DEVINFO_FILENAME DEVINFO.txt
#endif

void Device_printfMsg(void);

#endif


device.c



#include “device.h”
#include <stdint.h>
#include <stdio.h>

#define _STR(s) #s
#define MollocDefineToStr(mal) _STR(mal)

#include MollocDefineToStr(DEVINFO_FILENAME)

static const char devType[] = MollocDefineToStr(DEV_NAME);
static uint32_t devID = DEV_ID;
static const char devDName[] = MollocDefineToStr(DEV_NAME) “_” MollocDefineToStr(DEV_ID) “.local”;

void Device_printfMsg(void){
printf(“Device: %s\r\n” , devType);
printf(“DevID: %u\r\n” , devID);
printf(“DomainName: %s\r\n” , devDName);
}


![图片](https://img-blog.csdnimg.cn/img_convert/1df9e95b2c18fadfea01069e7331531e.png)


完美,设备相关信息全部都从外面的txt文件中读出来了,而且这个文件的文件名还是由刚刚才提到的可工程定制的宏配置的方式给出的。我们可以把其他几个设备的配置信息文件都补上。



// 设备名,字符串
#define DEV_NAME ABS
// 设备ID,U32
#define DEV_ID 34
// 设备名,字符串
#define DEV_NAME CBA
// 设备ID,U32
#define DEV_ID 33435
// 设备名,字符串
#define DEV_NAME LOL
// 设备ID,U32
#define DEV_ID 1234


![图片](https://img-blog.csdnimg.cn/img_convert/6d5f41fafe7bb901a5661d46b1d957e4.png)


好了,这样我们只要为所有设备各建立一个TXT的信息表,然后当需要切换不同的设备时就用前述方法改一下宏配置切换不同的文件名就好了。


要明白这个方法为什么能起作用,关键是要理解这一句:



#include MollocDefineToStr(DEVINFO_FILENAME)


我们知道,经过预处理器后,这一句就会变为



#include “DEVINFO.txt”


也许你会想:这是什么鬼,还可以include txt文件?我之前见得怎么都是include .h文件呀。 这是一个大大的误区。其实include从来没规定说一定要.h文件,其实可以是任何名字的,这个预处理器指令干的事情就是把include的文件不断递归的文本展开而已。


所以其实上面这句在经过预处理器后会被直接文本替换为对应的文件的内容,一字不差的那种。可能前后会加点注释信息。


所以这种成组绑定、十分固定的配置信息就很适合用这种方式解耦到不同的配置文件中去,按需导入即可。更进一步的,应该要专门为这些配置文件建一个文件夹进行管理。


而对于那种经常会独立更改的配置呢?


一两个的话可以通过之前说的预处理器宏定义的方式来搞定,但是一个稍微有点规模的项目总会涉及到好多好多的配置参数,这个时候就不适合都写在编译器选项里了。这个时候我会专门建一个工程配置文件,比如就叫app\_cfg.h,然后把整个工程中可能用到的宏配置都汇总在这里方便修改,这时之前那种可工程定制的宏写法就特别管用了:


app\_cfg.h


![img](https://img-blog.csdnimg.cn/img_convert/bf02279c7dbab69edeaf76977547655f.png)
![img](https://img-blog.csdnimg.cn/img_convert/2b5b6e69a824786cbd1d8e04a5bac67b.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

有点规模的项目总会涉及到好多好多的配置参数,这个时候就不适合都写在编译器选项里了。这个时候我会专门建一个工程配置文件,比如就叫app\_cfg.h,然后把整个工程中可能用到的宏配置都汇总在这里方便修改,这时之前那种可工程定制的宏写法就特别管用了:


app\_cfg.h


[外链图片转存中...(img-m0cMtJ3I-1715636274883)]
[外链图片转存中...(img-3pKimwjg-1715636274884)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值