宏定义 宏参数

2023-05-16

带参数的宏定义,利用宏参数创建字符串——#运算符

    看看以下两个宏定义:


#define PSQR(x) printf("The square of x is %d.\n",(x)*(x))

#define PSQR(x) printf("The square of "#x" is %d.\n",(x)*(x))  

    我们的目标是希望使用宏的时候将宏体中的x替换为宏参数,但是第一个宏定义是不能完成这样的目标的,因为引号中的x会被看做是普通文本,不可替换;我们可以采用#运算符来实现这样的功能。

 

PSQR(x)在两个不同的宏定义输出的结果如下:

The square of x is 4.

The square of 2 is 4.


define宏定义中的#,##,@#及\符号  


来源不详,seclu整理
1、# (stringizing)字符串化操作符。其作用是:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。
如:
    #define example(instr) printf("the input string is:\t%s\n",#instr)
    #define example1(instr) #instr
当使用该宏定义时:
example(abc); 在编译时将会展开成:printf("the input string is:\t%s\n","abc");
string str=example1(abc); 将会展成:string str="abc";
注意:
对空格的处理
a.忽略传入参数名前面和后面的空格。
   如:str=example1(   abc ); 将会被扩展成 str="abc";
b.当传入参数名间存在空格时,编译器将会自动连接各个子字符串,用每个子字符串中只以一个空格连接,忽略其中多余一个的空格。
   如:str=exapme( abc    def); 将会被扩展成 str="abc def";
 
2、## (token-pasting)符号连接操作符
宏定义中:参数名,即为形参,如#define sum(a,b) (a+b);中a和b均为某一参数的代表符号,即形式参数。
而##的作用则是将宏定义的多个形参成一个实际参数名。
如:
    #define exampleNum(n) num##n
    int num9=9;
使用:
int num=exampleNum(9); 将会扩展成 int num=num9;
注意:
1.当用##连接形参时,##前后的空格可有可无。
如:#define exampleNum(n) num ## n 相当于 #define exampleNum(n) num##n
2.连接后的实际参数名,必须为实际存在的参数名或是编译器已知的宏定义
    // preprocessor_token_pasting.cpp
    #include <stdio.h>
    #define paster( n ) printf_s( "token" #n " = %d", token##n )
    int token9 = 9; 
    int main() 
    { 
       paster(9); 
    }
运行结果:
token9 = 9
另外,如果##后的参数本身也是一个宏的话,##会阻止这个宏的展开。
  #define STRCPY(a, b)    strcpy(a ## _p, #b)
  int main() 
  { 
      char var1_p[20]; 
      char var2_p[30]; 
      strcpy(var1_p, "aaaa"); 
      strcpy(var2_p, "bbbb"); 
      STRCPY(var1, var2); 
      STRCPY(var2, var1); 
      printf("var1 = %s\n", var1_p); 
      printf("var2 = %s\n", var2_p); 
      return 0; 
      
      STRCPY(STRCPY(var1,var2),var2); 
      
  }
 
3、@# (charizing)字符化操作符。
只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。作用是将传的单字符参数名转换成字符,以一对单引用括起来。
    #define makechar(x)  #@x
    a = makechar(b);
展开后变成了:
a= 'b';
4、\ 行继续操作符
当定义的宏不能用一行表达完整时,可以用"\"表示下一行继续此宏的定义。


另:关于其他网友对##和#的补充
1. 简单的说,“##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。
   其中,分隔的作用类似于空格。我们知道在普通的宏定义中,预处理器一般把空格
   解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,
   被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些
   ##来替代空格。
   另外一些分隔标志是,包括操作符,比如 +, -, *, /, [,], ...,所以尽管下面的
   宏定义没有空格,但是依然表达有意义的定义: define add(a, b)  a+b
   而其强制连接的作用是,去掉和前面的字符串之间的空格,而把两者连接起来。
 
2. 举列 -- 试比较下述几个宏定义的区别
   #define A1(name, type)  type name_##type##_type //或 
   #define A2(name, type)  type name##_##type##_type
   A1(a1, int);  
   A2(a1, int);    
解释:
        1) 在第一个宏定义中,"name"和第一个"_"之间,以及第2个"_"和第二个
   "type"之间没有被分隔,所以预处理器会把name_##type##_type解释成3段:
   “name_”、“type”、以及“_type”,这中间只有“type”是在宏前面出现过
    的,所以它可以被宏替换。
        2) 而在第二个宏定义中,“name”和第一个“_”之间也被分隔了,所以
   预处理器会把name##_##type##_type解释成4段:“name”、“_”、“type”
   以及“_type”,这其间,就有两个可以被宏替换了。
        3) A1和A2的定义也可以如下:
         #define A1(name, type)  type name_  ##type ##_type
         //<##前面随意加上一些空格>
         #define A2(name, type)  type name ##_ ##type ##_type
结果是## 会把前面的空格去掉完成强连接,得到和上面结果相同的宏定义
3. 其他相关 -- 单独的一个 #
   至于单独一个#,则表示 对这个变量替换后,再加双引号引起来。比如
#define  __stringify_1(x)   #x
那么
      __stringify_1(linux)   <==>  "linux"
所以,对于MODULE_DEVICE_TABLE
    #define MODULE_DEVICE_TABLE(type,name)
        MODULE_GENERIC_TABLE(type##_device,name) 
    #define MODULE_GENERIC_TABLE(gtype,name)
        extern const struct gtype##_id __mod_##gtype##_table
        __attribute__ ((unused, alias(__stringify(name))))
得到
      MODULE_DEVICE_TABLE(usb, products)
                            
    <==> MODULE_GENERIC_TABLE(usb_device,products)
    <==> extern const struct usb_device_id __mod_usb_device_table
             __attribute__ ((unused, alias("products")))
注意到alias attribute需要一个双引号,所以在这里使用了__stringify(name)来
给name加上双引号。另外,还注意到一个外部变量"__mod_usb_device_table"被alias
到了本驱动专用的由用户自定义的变量products<usb_device_id类型>。这个外部变量
是如何使用的,更多的信息请参看《probe()过程分析》。
4. 分析方法和验证方式 -- 编写一个简单的C程序
   用宏定义一个变量,同时用直接方式定义一个相同的变量,编译报告重复定义;
   用宏定义一个变量,直接使用该宏定义的变量名称,编译通过且运行结果正确;
   使用printf打印字符串数据。printf("token macro is %s", __stringify_1(a1));

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

宏定义 宏参数 的相关文章

  • 什么是localhost(127.0.0.1)?

    什么是本地主机 xff1f 当你在计算机上 ping IP地址时 xff0c 你尝试联系互联网上的另一台计算机 xff0c 但是当你ping IP地址127 0 0 1时 xff0c 你正在与本地主机通信 localhost 始终是你自己的
  • OpenCV图像处理技术——图像直方图

    OpenCV图像处理技术 图像直方图 Fu Xianjun All Rights Reserved 图像直方图 图像直方图是图像内灰度值的统计特性与图像灰度值之间的函数 xff0c 直方图统计图像内各个灰度级出现的次数 直方图是数值数据分布
  • 【wireshark】Ubuntu 安装 wireshark 以及 wireshark 过滤器的使用

    目录 1 安装wireshark 2 wireshark 过滤器比较符号 3 wireshark 过滤方式 1 根据 IP 地址过滤 2 根据端口号过滤 3 根据报文长度过滤 4 HTTP协议过滤 参考文章链接 xff1a Wireshar
  • C++学习001:对象、可扩展性、编译型语言、相较于C的变化

    1 面向对象 基于对象 基于过程的区别 基于过程 xff1a 从上到下依次执行 xff0c C语言 cpp额外实现了基于对象和面向对象 基于对象 xff1a 类作为结构 类的成员作为对象 基于对象就是通过封装对象方便了程序过程中发生的突发事
  • Arduino基础入门篇23—串口通讯

    在很多时候 xff0c Arduino需要和其他设备相互通讯 xff0c 而最常见最简单的方式就是串口通讯 本篇介绍Arduino硬件串口通讯 xff0c 了解相关函数的使用 1 硬件串口 在PC机上最常见的串行通讯协议是RS 232串行协
  • 基于Qt的车载GPS监控系统(7)数据记录

    基于Qt的车载GPS监控系统 xff08 7 xff09 数据记录 数据日志显示界面 系统数据记录功能实现说明 1 通过一个线程定时记录系统当前的温度数据 2 通过数据日志界面显示保存的日志数据 数据记录线程代码 span class hl
  • 嵌入式系统重定向printf的三种方法

    对printf 进行重定向的三种方法 方法1 使用MircoLib并重定义fputc方法2 停用半主机模式 xff0c 在MDK中使用标准库重定向printf 方法3 在Gcc中使用标准库重定向printf 1 MDK使用MircoLib并
  • Windows API程序入门学习(1)

    导航 Windows API程序入门学习 xff08 1 xff09 学习目的作业要求实现步骤参考书籍实现代码运行结果 Windows API程序入门学习 xff08 1 xff09 学习目的 了解 windows操作系统应用程序开发的基本
  • 嵌入式系统基础学习笔记(四)

    目录 一 GDB调试简例1 1 例11 2 例2 二 OpenCV入门2 1 在Windows下使用OpenCV 3 4 82 1 1 OpenCV的安装与配置2 1 2 一个简单的OpenCV对图片特效显示例子 2 2 在Ubuntu18
  • 计算机网络实验报告(二):Wireshark 实验

    文章目录 一 数据链路层二 网络层三 传输层四 应用层 本部分按照数据链路层 网络层 传输层以及应用层进行分类 xff0c 共有 10 个实验 需要使用协议分析软件 Wireshark 进行 xff0c 请根据简介部分自行下载安装 准备 请
  • 计算机网络实验报告(三):Cisco Packet Tracer 实验

    文章目录 一 CPT 软件使用简介二 直接连接两台 PC 构建 LAN三 用交换机构建 LAN四 交换机接口地址列表五 生成树协议 xff08 Spanning Tree Protocol xff09 六 路由器配置初步说明一说明二说明三说
  • 嵌入式系统应用开发实验(一)

    文章目录 一 设计半加器二 半加器组成全加器三 烧录进硬件 实验目的 xff1a 通过1位全加器的详细设计 xff0c 掌握原理图输入以及Verilog的两种设计方法 软件基于quartusII 13 0版本 xff0c 开发板基于Inte
  • 关于浏览器以及各种内置浏览器造成黑屏的解决办法

    楼主在使用Chrome Edge浏览器甚至Steam时经常会发生网页还没打开就会发生 浏览器黑屏 电脑黑屏 自动重启 的过程 xff0c 困扰许久 网上的大部分教程是在浏览器设置里面关闭 硬件加速 功能 甚至于重装系统 但是经过实测 xff
  • 【FFTW库】编译生成 x86、arm 环境下的FFTW库

    FFTW是一个快速计算离散傅里叶变换的标准C语言程序集 xff0c 可计算一维或多维实和复数据以及任意规模的DFT 下面主要介绍的是 x86 环境下 FFTW库的编译过程 xff0c arm环境下的编译过程和FFTW类似 xff0c 不同之
  • C语言学习笔记w2d4

    文章目录 流程控制二循环语句gotowhile练习 do whilefor breakcontinue 作业 流程控制二 循环语句 循环的开始条件 循环的控制条件 循环的结束条件 goto 无条件跳转 xff0c 跳转到指定位置执行 xff
  • C语言学习笔记w2d5

    文章目录 数组一维数组练习字符数组字符串输入与输出 练习 多维数组 作业 数组 一维数组 用一个变量来存储具有一定关系的数据 xff0c 的数据集合叫数组 其中存储的变量是数组元素 a span class token punctuatio
  • Linux基础与C高级w3d4:linux的文件管理(续)、了解shell编程

    管道 作用 xff1a 把一个命令的结果作为另一个命令的输入参数 符号 xff1a 用法 xff1a ls grep test 用户管理 切换用户 xff1a su 用户名 修改用户密码 xff1a sudo passwd 用户 注册用户
  • ARM:day4

    ARM 的接口技术 裸机编程 例 xff1a LED灯 根据电路图找到 LED 找到控制的管脚 GPX2 7根据芯片手册 找到需要配置的地址空间地址 和使能值写汇编代码 编译工具 arm gcc 安装方式 把 bin 添加到PATH bas
  • ARM:day5

    ARM 的接口技术 串口 UART 工作模式 全双工发送二进制 ASCII码 两个设备通过各自的定时器来接收数据 空闲时拉高电平 要开始发数据时起始位拉低 0 发数据 一般是 8 位带一位校验位 结束发送 拉高电平回到空闲状态 缺点 特点
  • Ubuntu上igraph的安装教程

    暑假终于到了 xff0c 距离上一篇博客应该有3个月的时间了 xff0c 没有学期末的忙忙碌碌 xff0c 接下来会陆陆续续地回顾自己学过的和将学的东西一并整理成博客 关于igraph 因为课程需要 xff0c 在信息资源管理的课程上曾学过

随机推荐