0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【RT-Thread学习笔记】一种C语言宏定义的写法

嵌入式物联网开发 来源:嵌入式物联网开发 作者:嵌入式物联网开发 2022-07-30 13:41 次阅读

文章目录

  • 1 写在前面
  • 2 问题需求
  • 3 代码实践
    • 3.1 编写代码
    • 3.2 结果验证
  • 4 经验总结
  • 5 参考链接
  • 6 更多分享


1 写在前面

宏定义在 C语言中,是一种很常见的语法;经常阅读开源代码,你会发现,使用好C语言的宏定义,真的可以写出更加整洁,可读性非常高的高质量代码。

本文将描述一个需要使用宏定义技巧来解决的问题场景,希望对大家理解和使用C语言的宏定义有所帮助和提高。

2 问题需求

最近恰好在项目开发的过程中,遇到了一个有关宏定义的问题。项目运用的背景如下:

项目中有个头文件中定义了一个宏定义,比如是 #define CFG_LOGGER_NAME uart
然后,在某个C文件中需要将这个宏定义转换成对应的字符串类型,即为 “uart” ;很明显,如果按以下的几种方式定义,肯定得不到期望的结果:

方式1: #define CFG_LOGGER_NAME_STR  "CFG_LOGGER_NAME"

方式2: #define CFG_LOGGER_NAME_STR  #CFG_LOGGER_NAME

方式3: #define CFG_LOGGER_NAME_STR  ##CFG_LOGGER_NAME

3 代码实践

3.1 编写代码

为了解决这个问题,特意再次去查看了有关C语言宏定义的语法,终于找到了解决方法,具体的思路是,需要用一个 “中间宏函数” 做转换,我们用代码来实践一下。

#include 

#include  

#define TEST 						uart
#define TO_STR(x) 					#x

#define CFG_LOGGER_NAME				uart 
#define TO_STRING(x)				#x
#define _CFG_LOGGER_NAME_STR(x)		TO_STRING(x)	 			
#define CFG_LOGGER_NAME_STR 		_CFG_LOGGER_NAME_STR(CFG_LOGGER_NAME) 

/* 这三种都达不到需求 */
#define CFG_LOGGER_NAME_STR1  		"CFG_LOGGER_NAME"

/* 语法错误:error: stray ‘#’ in program */
//#define CFG_LOGGER_NAME_STR2  	#CFG_LOGGER_NAME

/* 语法错误: error: '##' cannot appear at either end of a macro expansion */
//#define CFG_LOGGER_NAME_STR3  	##CFG_LOGGER_NAME

int main(void)
{
	printf("\r\n%s\r\n", TO_STR(TEST));

	printf("\r\n%s\r\n", CFG_LOGGER_NAME_STR);
	
	printf("\r\n%s\r\n", CFG_LOGGER_NAME_STR1);
	
	//printf("\r\n%s\r\n", CFG_LOGGER_NAME_STR2);
	
	//printf("\r\n%s\r\n", CFG_LOGGER_NAME_STR3);

	return 0;
}

3.2 结果验证

验证环境如下:

recan@ubuntu:~$ uname -a
Linux ubuntu 5.4.0-65-generic #73-Ubuntu SMP Mon Jan 18 17:25:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
recan@ubuntu:~$ 
recan@ubuntu:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with:
Thread model: posix
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1) 

代码编译:

gcc -o test test.c

结果运行:

recan@ubuntu:~$ ./test

TEST

uart

CFG_LOGGER_NAME

查看宏定义展开后的预处理文件:

recan@ubuntu:~$ gcc -E -o test.i test.c | tail -n 20 test.i
# 499 "/usr/include/string.h" 3 4

# 4 "test.c" 2
# 22 "test.c"

# 22 "test.c"
int main(void)
{
 printf("\r\n%s\r\n", "TEST");

 printf("\r\n%s\r\n", "uart");

 printf("\r\n%s\r\n", "CFG_LOGGER_NAME");

 return 0;
}

我们可以看到宏代码的展开是符合我们的预期的,也只有CFG_LOGGER_NAME_STR 这一种写法是满足我们问题需求的。

4 经验总结

  • 宏定义看似很简单,没实践出来的时候,有时候会想不通为什么会这么被展开?
  • 在gcc编译器下查看宏定义被展开的内容使用的是-E选项。
  • C语言宏定义中的 “#” 和 “##” 是有特殊用法的,必须要用于带参数的宏定义中,否则会报语法错误。
  • 留个疑问:为何加了一个中间宏函数转了一道手,就能得到预期的内容?

5 参考链接

  • C语言的宏定义
  • 带参数和不带参数的宏定义

6 更多分享

架构师李肯

一个专注于嵌入式IoT领域的架构师。有着近10年的嵌入式一线开发经验,深耕IoT领域多年,熟知IoT领域的业务发展,深度掌握IoT领域的相关技术栈,包括但不限于主流RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、主流IoT云平台的对接、嵌入式IoT系统的架构设计等等。拥有多项IoT领域的发明专利,热衷于技术分享,有多年撰写技术博客的经验积累,连续多月获得RT-Thread官方技术社区原创技术博文优秀奖,荣获CSDN博客专家、CSDN物联网领域优质创作者、2021年度CSDN&RT-Thread技术社区之星、RT-Thread官方嵌入式开源社区认证专家、RT-Thread 2021年度论坛之星TOP4、华为云云享专家(嵌入式物联网架构设计师)等荣誉。坚信【知识改变命运,技术改变世界】!

欢迎关注我的github仓库01workstation,日常分享一些开发笔记和项目实战,欢迎指正问题。

同时也非常欢迎关注我的专栏,有问题的话,可以跟我讨论,知无不答,谢谢大家。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    180

    文章

    7530

    浏览量

    128555
  • 宏定义
    +关注

    关注

    0

    文章

    48

    浏览量

    8927
  • RT-Thread
    +关注

    关注

    31

    文章

    1148

    浏览量

    38868
收藏 人收藏

    评论

    相关推荐

    RT-Thread学习笔记】使用scons命令生成静态库

    RT-Thread学习笔记】如何使用scons 命令中buildlib的生成静态库?
    的头像 发表于 07-27 09:13 4755次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】使用scons命令生成静态库

    RT-Thread学习笔记】ARM汇编基础的三大块知识

    RT-Thread学习笔记】ARM汇编基础的三大块知识
    的头像 发表于 07-30 10:59 1605次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】ARM汇编基础的三大块知识

    RT-Thread学习笔记

    程序运行时,RT-Thread才会从系统堆中申请分配这段内存空间,当不需要使用该线程时,调用rt_thread_delete函数就会将这段申请的内存空间重新释放到内存堆中。这两方式各有利弊,静态
    发表于 11-18 20:08

    RT-Thread学习笔记和finsh shell配置方法

    配置。图解详细步骤:RT-Thread学习笔记--finsh shell配置方法 RT-Thread学习
    发表于 02-12 00:01

    RT-Thread学习笔记

    程序运行时,RT-Thread才会从系统堆中申请分配这段内存空间,当不需要使用该线程时,调用rt_thread_delete函数就会将这段申请的内存空间重新释放到内存堆中。这两方式各有利弊,静态
    发表于 03-25 11:19

    RT-Thread Nano入门学习笔记

    RT-Thread Nano入门学习笔记
    发表于 11-26 12:36 20次下载
    <b class='flag-5'>RT-Thread</b> Nano入门<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>

    RT-Thread 内核学习笔记 - 理解defunct僵尸线程

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:19 8次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 理解defunct僵尸线程

    RT-Thread 内核学习笔记 - 设备模型rt_device的理解

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:19 8次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 设备模型<b class='flag-5'>rt</b>_device的理解

    RT-Thread 内核学习笔记 - 内核对象链表结构深入理解

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:23 6次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 内核对象链表结构深入理解

    RT-Thread 内核学习笔记 - 内核对象操作API

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:26 7次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 内核对象操作API

    RT-Thread学习笔记 RT-Thread的架构概述

    RT-Thread 简介 作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread 的魅力和它相较于其他同类型 RTOS
    的头像 发表于 07-09 11:27 3996次阅读
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b> <b class='flag-5'>RT-Thread</b>的架构概述

    RT-Thread学习笔记】Makefile的FORCE

    RT-Thread学习笔记】十分钟学会Makefile的FORCE
    的头像 发表于 07-30 13:55 1652次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】Makefile的FORCE

    RT-Thread学习笔记】如何抓取终端的网络报文

    RT-Thread学习笔记】如何抓取终端的网络报文?
    的头像 发表于 07-30 13:57 2161次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】如何抓取终端的网络报文

    RT-Thread学习笔记】用memwatch排除内存泄露

    RT-Thread学习笔记】使用memwatch排除内存泄露
    的头像 发表于 07-30 14:01 1726次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】用memwatch排除内存泄露

    基于RT-Thread Studio学习

    前期准备:从官网下载 RT-Thread Studio,弄个账号登陆,开启rt-thread学习之旅。
    的头像 发表于 05-15 11:00 2580次阅读
    基于<b class='flag-5'>RT-Thread</b> Studio<b class='flag-5'>学习</b>