宏定义详解

2023-05-16

宏定义有无参数宏定义带参数宏定义两种。
  无参数的宏定义的一般形式为
            # define 标识符 字符序列
其中# define之后的标识符称为宏定义名(简称宏名),要求宏名与字符序列之间用空格符分隔。这种宏定义要求编译预处理程序将源程序中随后所有的定名的出现(注释与字符串常量中的除外)均用字符序列替换之。前面经常使用的定义符号常量是宏定义的最简单应用。如有:
            # define TRUE 1
            # define FALSE 0
则在定义它们的源程序文件中,凡定义之后出现的单词TRUE将用1替代之;出现单词FALSE将用0替代之。
       在宏定义的#之前可以有若干个空格、制表符,但不允许有其它字符。宏定义在源程序中单独另起一行,换行符是宏定义的结束标志。如果一个宏定义太长,一行不 够时,可采用续行的方法。续行是在键人回车符之前先键入符号"\"。注意回车要紧接在符号"\"之后,中间不能插入其它符号。
      宏定义的有效范围称为宏定义名的辖域,辖域从宏定义的定义结束处开始到其所在的源程序文件末尾。宏定义名的辖域不受分程序结构的影响。可以用预处理命令#undef终止宏定义名的辖域。
  在新的宏定义中,可以使用前面已定义的宏名。例如,
             # define R 2.5
             # define PI 3.1415926
             # define Circle 2*PI*R
             # define Area PI* R * R
程序中的Circle被展开为2*3.1415926* 2.5, Area被展开为3.1415926*2.5*2.5。
     如有必要,宏名可被重复定义。被重复定义后,宏名原先的意义被新意义所代替。

 

     通常,无参数的宏定义多用于定义常量。程序中统一用宏名表示常量值,便于程序前后统一,不易出错,也便于修改,能提高程序的可读性和可移植性。特别是给数组元素个数一个宏定义,并用宏名定义数组元素个数能部分弥补数组元素个数固定的不足。
      注意:预处理程序在处理宏定义时,只作字符序列的替换工作,不作任何语法的检查。如果宏定义不当,错误要到预处理之后的编译阶段才能发现。宏定义以换行结束,不需要分号等符号作分隔符。如有以下定定义:
   # define PI 3.1415926;
原希望用PI求圆的周长的语句
   c=2*PI*r;
经宏展开后,变成
   c=2*3.1415926*r;
这就不能达到希望的要求。
   带参数宏定义进一步扩充了无参数宏定义的能力,在字符序列替换同时还能进行参数替换。带参数定定义的一般形式为
   # define 标识符(参数表)字符序列
其中参数表中的参数之间用逗号分隔,字符序列中应包含参数表中的参数。在定义带参数的宏时,宏名标识符与左圆括号之间不允许有空白符,应紧接在一起,否则变成了无参数的宏定义。如有宏定义:
   # define MAX(A,B) ((A) > (B)?(A):(B))

则代码 y= MAX( p+q, u+v)将被替换成 y=((p+q) >(u+v)?(p+q):(u+v)。
           程序中的宏调用是这样被替换展开的,分别用宏调用中的实在参数字符序列(如p+q和u+V) 替换宏定义字符序列中对应所有出现的形式参数(如用p+q替代所有形式参数A,用u+V替代所有形式参数B),而宏定义字符序列中的不是形式参数的其它字 符则保留。这样形成的字符序列,即为宏调用的展开替换结果。宏调用提供的实在参数个数必须与宏定义中的形式参数个数相同。
       注意:宏调用与函数调用的区别。函数调用在程序运行时实行,而宏展开是在编译的预处理阶段进行;函数调用占用程序运行时间,宏调用只占编译时间;函数调用 对实参有类型要求,而宏调用实在参数与宏定义形式参数之间没有类型的概念,只有字符序列的对应关系。函数调用可返回一个值,宏调用获得希望的C代码。另 外,函数调用时,实参表达式分别独立求值在前,执行函数体在后。宏调用是实在参数字符序列替换形式参数。替换后,实在参数字符序列就与相邻的字符自然连 接,实在参数的独立性就不一定依旧存在。如下面的宏定义:
   # define SQR(x) x*x
希望实现表达式的平方计算。对于宏调用
   P=SQR(y)
能得到希望的宏展开p= y*y。但对于宏调用q=SQR(u+v)得到的宏展开是q=u+V*u+V。显然,后者的展开结果不是程序设计者所希望的。为能保持实在参数替换后的独立性,应在宏定义中给形式参数加上括号。进一步,为了保证宏调用的独立性,作为算式的宏定义也应加括
号。如 SQR宏定义改写成:
   # define SQR((x)*(x))
才是正确的宏定义。

      对于简短的表达式计算函数,或为了提高程序的执行效率、避免函数调用时的分配存储单元、保留现场、参数值传递、释放存储单元等工作。可将函数定义改写成宏定义。所以合理使用宏定义,可以使程序更简洁。


 

使用一些宏跟踪调试

A N S I标准说明了五个预定义的宏名。它们是:

_ L I N E _ (两个下划线),对应%d

_ F I L E _    对应%s

_ D A T E _   对应%s

_ T I M E _   对应%s

_ S T D C _

如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序

也许还提供其它预定义的宏名。

_ L I N E _及_ F I L E _宏指令在有关# l i n e的部分中已讨论,这里讨论其余的宏名。

_ D AT E _宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。

源代码翻译到目标代码的时间作为串包含在_ T I M E _中。串形式为时:分:秒。

如果实现是标准的,则宏_ S T D C _含有十进制常量1。如果它含有任何其它数,则实现是

非标准的。

可以定义宏,例如:

当定义了_DEBUG,输出数据信息和所在文件所在行

#ifdef _DEBUG

#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%s”,date,_LINE_,_FILE_)

#else

      #define DEBUGMSG(msg,date)

#endif

20,宏定义防止使用是错误

用小括号包含。

例如:#define ADD(a,b) (a+b)

用do{}while(0)语句包含多语句防止错误

例如:#difne DO(a,b) a+b;\

                   a++;

应用时:if(….)

          DO(a,b); //产生错误

        else

        

解决方法: #difne DO(a,b) do{a+b;\

                   a++;}while(0)


宏中"#"和"##"的用法
一、一般用法
我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起(这里说的是在预处理是对源文件的操作).
用法:
#include<cstdio>
#include<climits>
using namespace std;

#define STR(s)     #s
#define CONS(a,b) int(a##e##b)

int main()
{
printf(STR(vck));           // 输出字符串"vck"
printf("%d\n", CONS(2,3)); // 2e3 输出:2000
return 0;
}

二、当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用''#''或''##''的地方宏参数是不会再展开.

1, 非''#''和''##''的情况
#define TOW      (2)
#define MUL(a,b) (a*b)

printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));
这行的宏会被展开为:
printf("%d*%d=%d\n", (2), (2), ((2)*(2)));
MUL里的参数TOW会被展开为(2).

2, 当有''#''或''##''的时候
#define A          (2)
#define STR(s)     #s
#define CONS(a,b) int(a##e##b)

printf("int max: %s\n", STR(INT_MAX));    // INT_MAX #include<climits>
这行会被展开为:
printf("int max: %s\n", "INT_MAX");

printf("%s\n", CONS(A, A));               // compile error
这一行则是:
printf("%s\n", int(AeA));

INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏.
加这层宏的用意是把所有宏的参数在中间层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.

#define A           (2)
#define _STR(s)     #s
#define STR(s)      _STR(s)          // 转换宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b)   _CONS(a,b)       // 转换宏

printf("int max: %s\n", STR(INT_MAX));          // INT_MAX,int型的最大值,为一个变量 #include<climits>
输出为: int max: 0x7fffffff
STR(INT_MAX) --> _STR(0x7fffffff) 然后再转换成字符串;

printf("%d\n", CONS(A, A));
输出为:200
CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))

三、''#''和''##''的一些应用特例
1、合并匿名变量名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示该行行号;
第一层:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第二层:                        --> ___ANONYMOUS1(static int, _anonymous, 70);
第三层:                        --> static int _anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;

2、填充结构
#define FILL(a)   {a, #a}

enum IDD{OPEN, CLOSE};
typedef struct MSG{
IDD id;
const char * msg;
}MSG;

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相当于:
MSG _msg[] = {{OPEN, "OPEN"},
{CLOSE, "CLOSE"}};

3、记录文件名
#define _GET_FILE_NAME(f)   #f
#define GET_FILE_NAME(f)    _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

4、得到一个数值类型所对应的字符串缓冲大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof "0x7fffffff"];
这里相当于:
char buf[11];

 

转载于:https://www.cnblogs.com/aoyihuashao/archive/2010/01/14/1647896.html

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

宏定义详解 的相关文章

  • 爬取需要登录的网站

    爬虫在采集网站的过程中 xff0c 部分数据价值较高的网站 xff0c 会限制访客的访问行为 这种时候建议通过登录的方式 xff0c 获取目标网站的cookie xff0c 然后再使用cookie配合代理IP进行数据采集分析 1 使用表单登
  • Makefile和Cmake的联系与区别

    CMake是一种跨平台编译工具 xff0c 比make更为高级 xff0c 使用起来要方便得多 CMake主要是编写CMakeLists txt文件 xff0c 然后用cmake命令将CMakeLists txt文件转化为make所需要的m
  • STM32内部寄存器、储存器、C对寄存器的封装

    STM32内部寄存器 储存器 C对寄存器的封装 STM32的地址空间可以分为8个 xff0c 分别是Block0 Block7 xff0c 其中Block2是应用于外设的存储器 xff0c 也是主要学习的内容 存储器映射 存储器的地址由厂商
  • C++ STL中各种数据结构操作的时间复杂度比较

    C 43 43 STL中各种数据结构操作的时间复杂度比较 访问push back push front insert pop back pop front erace find listO n O 1 O 1 O 1 O 1 O 1 O 1
  • pom.xml 标签详解

    lt project xmlns 61 34 http maven apache org POM 4 0 0 34 xmlns xsi 61 34 http www w3 org 2001 XMLSchema instance 34 xsi
  • 解决“打开ArcGIS Server Manager”网页无反应为空白的情况

    问题 xff1a 装上arcgis serve 10后 xff0c 打开arcgis server manager页面返回空白 xff0c 用firefox显示 未找到元素 郁闷 xff0c 后来想尽各种办法 终于可以了 解决办法 xff1
  • 汇编语言教程-返回指令(RET)

    汇编语言教程 返回指令 RET 当子程序执行完时 xff0c 需要返回到调用它的程序之中 为实现此功能 xff0c 指令系统提供了一条专用的返回指令 其格式如下 xff1a RET RETN RETF Imm 子程序的返回在功能上是子程序调
  • 富斯/MC6接收机说明书

    正面 反面 1 PWM输出通道多达6个 xff0c 可以自由切换7种模式 xff0c 自由选择无刷 xff0c 有刷 xff0c 差速 xff0c 炫酷的RGB全彩灯带等 xff0c 自由玩耍 2 集成两个5A有刷电调 xff0c 通过模式
  • C++之struct构造函数(2010-10-19 15:04:47)

    C 43 43 之struct构造函数 2010 10 19 15 04 47 转载 标签 xff1a cpp struct 构造函数 校园 分类 xff1a C C PlusPlus 在网络协议 通信控制 嵌入式系统的C C 43 43
  • 汉字转拼音

    原创的兄弟 xff0c 看来是费了不少功夫 在此谢过了 代码如下 xff1a public class hanzi to pinyin1 private static readonly string Allhz 61 new string
  • 什么是功能性需求和非功能性需求

    需求定义 xff1a 需求 xff08 requirement xff09 就是系统 xff08 更广义的说法是项目 xff09 必须提供的能力和必须遵从的条件 需求分类 xff1a 1 在一般使用中 xff0c 需求按照功能性 xff08
  • 卷三、七言古诗

    卷三 七言古诗 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 登幽州台歌 作者 xff1a 陈子昂 前不见古人
  • 卷五、五言律诗

    卷五 五言律诗 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 经邹鲁祭孔子而叹之 作者 xff1a 唐玄宗 夫子
  • 卷六、七言律诗

    61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 黄鹤楼 作者 xff1a 崔颢 昔人已乘黄鹤去 xff0c 此地空
  • 计算两个经纬度间的距离(c++)

    double D jw double wd1 double jd1 double wd2 double jd2 double x y out double PI 61 3 14159265 double R 61 6 371229 1e6
  • C语言库函数及示例

    函数名 abort 功 能 异常终止一个进程 用 法 void abort void 程序例 include lt stdio h gt include lt stdlib h gt int main void printf 34 Call
  • Json风格指南

    英文版 xff1a http google styleguide googlecode com svn trunk jsoncstyleguide xml 翻译 xff1a Darcy Liu 简介 该风格指南是对在Google创建JSON
  • C中__FILE__ __LINE__的用法

    include lt stdio h gt void main void printf 34 File s Successfully reached line d n 34 FILE LINE Other statements here l
  • MC6C迈克/FLYSKY富斯/WFLY2天地飞二代接收机远程刷固件教程

    1 安装ch341的驱动程序 请找ch341卖家要或百度找 2 ch341的跳线跳到usb To ttl 如能本身只有TTL刷机的功能的板子 xff0c 像CH340一般只有usb to ttl的功能 xff0c 这一步可以不做 3 接收机
  • STM32入门系列-使用C语言封装寄存器

    前面介绍了存储器映射 寄存器和寄存器映射 xff0c 这些都是为了介绍使用 C语言封装寄存器做铺垫 这里我们通过一个实例来对 C 语言封装寄存器进行介绍 具体实例 xff1a 控制 GPIOC 端口的第 0 管脚输出一个低电平 首先我们需要

随机推荐

  • *** buffer overflow detected ***异常

    一次在linux上编译程序报错 xff1a buffer overflow detected TAppEncoderStaticSADBS terminated 排查原因发现是sprintf读取时数组长度不够 xff0c 将数组长度由50增
  • 利用火狐浏览器脚本功能_充分利用Firefox

    利用火狐浏览器脚本功能 Firefox 0 8的发布消息是凤凰网 Firebird Mozilla浏览器系列中的最新版本 xff0c 目前 xff0c Web开发社区对此感到非常关注 该发行版标志着Mozilla项目独立浏览器的第三个也是最
  • 串口接收无定长数据

    1 原理 xff1a 1 使能串口接收中断 定时器中断 xff1b 2 在串口第一次进入到中断后 xff0c 使能定时器计时 xff1b 3 在串口每次进入中断后 xff0c 清空定时器 xff1b 4 当定时器溢出时 xff0c 判定数据
  • OpenWRT 小记

    查看openwrt内核版本 xff1a cat proc version uname r 生成配置文件 xff1a config generate 查看DHCH 已经分配的IP cat var dhcp leases 分割cat tmp d
  • OpenWrt OpenMPTCProuter feed

    echo 34 src git OpenMPTCProuter https github com Ysurac openmptcprouter feeds git 34 gt gt feeds conf default echo 34 sr
  • openwrt路由器接华为E3372(E8372)网卡实现4G转有线和WIFI

    Hilink 在openwrt系统中安装kmod usb net rndis kmod usb net kmod usb2 usb modeswitch kmod usb net cdc ether 安装完成后 xff0c 把E3372 x
  • windows 10 内存居高不下,实际没开多少进程

    windows 10 内存居高不下 xff0c 实际没开多少进程 关闭快速启动 就好了
  • opkg list 报错

    opkg list Collected errors opkg conf load Could not lock var lock opkg lock Resource temporarily unavail echo 34 nameser
  • openwrt opkg install 强制替换安装

    查询 opkg list installed grep XXX opkg install XXX ipk force downgrade
  • stm32 硬件spi半双工三线的一些研究心得

    a7105可以使用四线spi 或者3线spi 但是之前都是使用3线的软件模拟的三线spi的 xff0c 所以不想改其它代码了 xff0c 就想可以提高一个spi的读写速度 xff0c 原来软件方式的读写速度 xff0c 在48Mhz的03x
  • Openwrt tftp刷机

    第一次写论坛 xff0c 今天早上才拿到路由器 开始学习openwrt 之前学过嵌入式Linux arm 移植 xff0c 开始正题 xff1a 拿到开发板后 xff0c 就开始烧写自己编译的 bin文件 xff0c 在烧写的过程中出现了问
  • openwrt ipk 安装 luci 界面

    试试看可行不 慢慢更新 opkg update 更新 opkg list grep svn
  • OpenWRT php 安装

    一 安装PHP opkg update opkg install php5 php5 mod apc opkg install php5 mod gd php5 mod session opkg install php5 mod pdo m
  • ESP8266 固件擦除

    折腾了两天 真是醉了 首先确认安装 python python2是否安装 python2 version sudo apt isntall python pip 安装pip和他的许多其他依赖 pip 9 0 1 from usr lib p
  • 第二次实验报告:使用Packet Tracer分析应用层协议

    姓名 xff1a 刘钰学号 xff1a 201821121036班级 xff1a 计算1812 1 实验目的 熟练使用Packet Tracer工具 分析抓到的应用层协议数据包 xff0c 深入理解应用层协议 xff0c 包括语法 语义 时
  • C++的类与C语言结构体比较

    C 43 43 的类与C语言结构体比较 C 43 43 的类与C语言结构体比较 一 结构体 xff0c 类的介绍二 结构体和类的具体区别1 C语言对结构体数组初始化 必须要在定义时初始化 xff1a 2 C 43 43 的类的初始化 构造函
  • CPP-网络/通信:经典HTTP协议详解

    2008 11 03 09 11 by Hundre 266688 阅读 23 评论 收藏 编辑 转自 xff1a http blog csdn net gueter archive 2007 03 08 1524447 aspx Auth
  • 串口编程3:使用串口读取GPS信息

    关于GPS的使用 xff0c 参考 本文主要参考的博客 xff0c 在此表示感谢 xff01 xff01 xff01 主函数 主函数gps main c xff0c 这里便涉及到了串口的打开 xff0c 读操作 xff0c 以及调用了串口设
  • 基于单片机语音智能导盲仪仿真设计-毕设课设资料

    资料下载地址 1110 xff08 百度网盘 xff09 xff1a 点击下载 包含超声波传感器检测障碍物 xff0c 温度传感器检测当前温度 可以通过按键设置距离报警范围 xff0c 报警装置通过声光报警 包含的电路有电源电路 显示电路
  • 宏定义详解

    宏定义有无参数宏定义和带参数宏定义两种 无参数的宏定义的一般形式为 define 标识符 字符序列 其中 define之后的标识符称为宏定义名 简称宏名 xff0c 要求宏名与字符序列之间用空格符分隔 这种宏定义要求编译预处理程序将源程序中