当前位置: 首页 > news >正文

C语言宏中“#”和“##”的用法

文章目录

  • 前言
  • 一、(#) 字符串化操作符
    • C语言会自动将相邻的两个字符串合并:
    • 参数详解
    • 对于#的参数,即便是另一个宏,也不展开,仍然作为字符串字面信息输出。
  • 二、(##)符号连接操作符
    • 常见用法1:在结构体定义中的妙用
    • 常见用法2:统一宏替换


前言

使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起。只有充分了解到其中的用法,才能更好的看懂别人的代码以及自己手到擒来的使用。

#和##都属于预处理标记,即编译器会在预处理阶段进行相关的替换或者处理

一、(#) 字符串化操作符

"#"的功能是将其后面的宏参数进行字符串化操作,等同于把后面的宏变量加上双引号。
宏定义函数的参数与预处理标记 ‘#’ 之间出现的每一个空格都会被删除,并删除第一个预处理标记之前和最后一个预处理标记之后的空白字符,但是宏定义函数参数中的空格会保留。
空参数转化为空,即宏定义函数入参为空,那么展开的时候也为空。

#include <stdio.h>#define MAKE_STR(s) (#s)int main(void)
{printf("hello\n");printf(MAKE_STR(hello\n));return 0;
}输出结果:
hello
hello

它具体是怎么实现的呢?其实很简单就是在其宏变量被替换,在其左右两侧加上双引号。为了探究这个过程,我们在用gcc编译的时候加上-E选项来看下编译器的预处理过程:

int main(void)
{printf("hello\n");printf(("hello\n"));return 0;
}

C语言会自动将相邻的两个字符串合并:

#include <stdio.h>#define MAKE_STR(s) (#s"\n")int main(void)
{printf("hello\n");printf(MAKE_STR(hello));return 0;
}

参数详解

宏定义函数的参数与预处理标记 ‘#’ 之间出现的每一个空格都会被删除,并删除第一个预处理标记之前和最后一个预处理标记之后的空白字符,但是宏定义函数参数中的空格会保留。

#include <stdio.h>#define MAKE_STR(s) ("321" # s "654\n")int main(void)
{printf("hello world\n");printf(MAKE_STR(hello world));return 0;
}输出结果:
hello world
321hello world654

#和宏定义参数s之间的空格被删除掉了。字符串"321"和字符串"654"和# s之间的空格也被删除掉了。
但是宏参数hello world之间的空格并没有被删除。
注意:'#'只能用于传入参数的宏定义中,且必须置于宏定义体中的参数名前。

对于#的参数,即便是另一个宏,也不展开,仍然作为字符串字面信息输出。

#include<stdio.h>#define dprint(expr) printf(#expr"=%d\n",expr); int main() 
{  int a=20,b=10;dprint(a/b);return 0; 
}/*输出:a/b=2*/

二、(##)符号连接操作符

作用:"##"被称为预处理拼接标记,宏定义展开的时候,用来将其左右两边两个token连接为一个token。注意这里连接的对象是token就行,不一定是宏的变量。

#include <stdio.h>#define CONS(a,b)  int(a##e##b)
int main()
{printf("%d\n", CONS(2,3));  // 2e3 输出:2000return 0;
}输出结果:
2000

常见用法1:在结构体定义中的妙用

比较多开源代码中惯用的做法,相比常规的结构体定义法,确实省去很多重复的代码。

#include <stdio.h>#define DF_STRUCT(name) typedef struct tag##name name;\struct tag##name
DF_STRUCT(DevManage)
{int index;   //索引 int Access;  //权限//...  
};int main(int argc, char *argv[]) {DevManage stDevManage;stDevManage.index  = 1;stDevManage.Access = 666;printf("Dev Index :%d\n",stDevManage.index );printf("Dev Access:%d\n",stDevManage.Access );return 1;
}

常见用法2:统一宏替换

拼接标识符意味着符号的粒度更高,而这碎片化的符号进行有效的管理,就可以使得符号更加具有通用性和灵活性。
其实这种思想跟我们代码模块话是同样的道理。

#include <stdio.h>
#include <stdlib.h>//假如这是stm32库中的宏 
#define GPIO_Pin_0                 ((int)0x0001)  /*!< Pin 0 selected */
#define GPIO_Pin_1                 ((int)0x0002)  /*!< Pin 1 selected */
#define GPIO_Pin_2                 ((int)0x0004)  /*!< Pin 2 selected */
#define GPIO_Pin_3                 ((int)0x0008)  /*!< Pin 3 selected */#define USART1              ((int *) 0x1000)
#define USART2              ((int *) 0x2000)//拼接变量 
#define UARTX 1//最终的组合标识符 
#define UART1_CORE  USART1
#define UART1_RX    GPIO_Pin_0
#define UART1_TX    GPIO_Pin_1#define UART2_CORE  USART2
#define UART2_RX    GPIO_Pin_2
#define UART2_TX    GPIO_Pin_3//拼接过程 
#define _UARTX_CORE(uartx)   UART##uartx##_CORE 
#define UARTX_CORE(uartx)    _UARTX_CORE(uartx)#define _UARTX_RX(uartx)   UART##uartx##_RX
#define UARTX_RX(uartx)    _UARTX_RX(uartx) #define _UARTX_TX(uartx)   UART##uartx##_TX
#define UARTX_TX(uartx)    _UARTX_TX(uartx)int main(int argc, char *argv[]) {//组合标识符的使用 printf("0x%x\n",UARTX_CORE(UARTX));printf("0x%x\n",UARTX_RX(UARTX));printf("0x%x\n",UARTX_TX(UARTX));return 1;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 优先级队列的实现
  • 【uniapp】vue3+vite配置tailwindcss
  • 力扣热题100_链表_234_回文链表
  • ubuntu设置共享文件夹,非虚拟机,服务器版
  • XSS DOM漏洞复现 与DOM 破坏
  • ARM/Linux嵌入式面经(二四):国光电器
  • 雷达气象学(9)——反射率因子图分析(强对流篇)
  • 二十、观察者模式
  • 在postman设置请求里带动态token,看看这两种方法!
  • Python接口自动化之unittest单元测试
  • 深入理解指针(五)
  • 分享一个基于SpringBoot的戏剧戏曲科普平台的设计与实现(源码、调试、LW、开题、PPT)
  • 【观察者模式】设计模式系列: 实现与最佳实践案例分析
  • 前端案例:Alloy Team|腾讯全端项目(响应式)
  • 【项目】基于Vue3.2+ElementUI Plus+Vite 通用后台管理系统
  • CSS居中完全指南——构建CSS居中决策树
  • GraphQL学习过程应该是这样的
  • java第三方包学习之lombok
  • JAVA之继承和多态
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • js ES6 求数组的交集,并集,还有差集
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • node.js
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 闭包,sync使用细节
  • 大整数乘法-表格法
  • 基于 Babel 的 npm 包最小化设置
  • 讲清楚之javascript作用域
  • 解析 Webpack中import、require、按需加载的执行过程
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 听说你叫Java(二)–Servlet请求
  • 微信开源mars源码分析1—上层samples分析
  • raise 与 raise ... from 的区别
  • ​ssh免密码登录设置及问题总结
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #includecmath
  • (2024.6.23)最新版MAVEN的安装和配置教程(超详细)
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (LLM) 很笨
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (五)c52学习之旅-静态数码管
  • (一) 初入MySQL 【认识和部署】
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (转)树状数组
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • *p++,*(p++),*++p,(*p)++区别?
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .NET MAUI Sqlite数据库操作(二)异步初始化方法
  • .NET 中创建支持集合初始化器的类型