宏定义有无参数宏定义和带参数宏定义两种
宏定义有无参数宏定义和带参数宏定义两种。
无参数的宏定义的一般形式为
# 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被展开为23.1415926 2.5, Area被展开为3.14159262.52.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代码
|
实参表达式分别独立求值在前,执行函数体在后
|
实在参数字符序列替换形式参数,替换后,实在参数字符序列就与相邻的字符自然连 接,实在参数的独立性就不一定依旧存在。
|
- 函数调用在
程序运行时
实行,而宏展开是在编译的预处理阶段
进行; - 函数调用占用
程序运行时间
,宏调用只占编译时间
; - 函数调用
对实参有类型要求
,而宏调用实参
与宏定义形式参数
之间没有类型的概念
,只有字符序列的对应关系
。 - 函数调用可
返回一个值
,宏调用获得希望的C代码
。 - 函数调用时,实参表达式分别独立求值在前,执行函数体在后。宏调用是实在参数字符序列替换形式参数。替换后,实在参数字符序列就与相邻的字符自然连 接,实在参数的独立性就不一定依旧存在。如下面的宏定义:
# define SQR(x) x*x
希望实现表达式的平方计算。对于宏调用
P=SQR(y)
能得到希望的宏展开p= yy。但对于宏调用q=SQR(u+v)得到的宏展开是q=u+Vu+V。显然,后者的展开结果不是程序设计者所希望的。进一步,为了保证宏调用的独立性,作为算式的宏定义也应加括
号。如 SQR宏定义改写成:
# define SQR(x) (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
这行会被展开为:
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
输出为: 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]
本文转载自http://hi.baidu.com/ufo008ahw/blog/item/5e943d4f5f49513caec3abd1.html)
本文来自 叮当小菜 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/swee111/article/details/73275977?utm_source=copy
更新
//如果没有定义 HEADTEST_H_,继续往下
//否则,就退出
#ifndef _HEADTEST_H_
//定义 _HEADTEST_H_
#define _HEADTEST_H_//宏定义,预编译阶段,直接替换#endif
宏定义有无参数宏定义和带参数宏定义两种相关推荐
- python 装饰器 参数-[Python]写个带参数的装饰器
上篇文章 Python装饰器为什么难理解?从函数到装饰器一步一步介绍了Python装饰器的来由,不知你对装饰器理解了没有,强烈建议你自己动手写个装饰器应用到项目中加深理解.装饰器可以很简单,也可以很复 ...
- python 装饰器 参数-python函数装饰器之带参数的函数和带参数的装饰器用法示例...
本文实例讲述了python函数装饰器之带参数的函数和带参数的装饰器用法.分享给大家供大家参考,具体如下: 1. 函数带多个参数 # 普通的装饰器, 打印函数的运行时间 def decrator(fun ...
- python装饰器函数-python函数装饰器之带参数的函数和带参数的装饰器用法示例
本文实例讲述了python函数装饰器之带参数的函数和带参数的装饰器用法.分享给大家供大家参考,具体如下: 1. 函数带多个参数 # 普通的装饰器, 打印函数的运行时间 def decrator(fun ...
- python装饰器模式带参数_Python进阶(七)----带参数的装饰器,多个装饰器修饰同一个函数和递归简单案例(斐波那契数列)...
Python进阶(七)----带参数的装饰器,多个装饰器修饰同一个函数和递归简单案例(斐波那契数列) 一丶带参数的装饰器 def wrapper_out(pt): def wrapper(func): ...
- matlab参数群体赋值,matlab 带参数的矩阵赋值
可以借助于符号函数. 代码: % 以前只用matlab求不带参数的矩阵的逆, % 请问能不能用matlab求带参数矩阵的逆, % 参考书上说可以,但是不知道怎么定义参数, % 实际操作显示错误,说参数 ...
- url没有参数名怎么直接带参数_用30行Python爬虫带你看PLMM(划掉,喵星人)
偶尔写写爬虫也算是打磨无聊生活的一种方式了. 之前写了一个用100多行Python爬虫看世界的帖子,有兴趣的朋友可以看一下. 带你用100多行Python爬虫看看今天的世界(上) 带你用100多行Py ...
- response响应对象参数和方法是啥?怎么发送带请求头的请求(headers参数)怎么发送带参数的请求?cookieJar的转换方法是什么?
在python里所有变量都是对象 import requestsurl = 'http://www.baidu.com/'respone = requests.get(url)# 参数 # 响应的ur ...
- java登录代码带验证码实现_两种方法实现带验证码的用户登录
带验证码的登录效果图 登录成功时,记录用户名和访问次数 登录失败,弹出错误提示 方法一:应用request对象获取表单数据实现登录操作 代码结构: index.jsp源码及详解: 当表单被提交时,数据 ...
- java sort 第二个参数_详解java Collections.sort的两种用法
Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,它有两种参数形式: public static > void sort(List list) { l ...
- python连接access 参数太少_带参数的PypyODBC:[ODBC Microsoft Access Driver]参数太少。预期4...
我使用pypyodbc从access数据库中选择数据.我使用下面的查询,其中有三个指定的参数.在 我试过几种品种,但没有效果.我看不出我的语法有什么问题.在SELECT [Date], [Time], ...
最新文章
- python查询sqlserver视图_如题:sqlserver连接Oracle数据库,在sql查询分析器中查询oracle中的视图,根据时间字段查询,SQL语句...
- rms c语言 函数,C中任何更快的RMS值计算?
- JVM调优:G1三色标记算法
- Python基础day05【函数(局部变量、全局变量、多函数执行流程、函数返回值、函数参数)、拆包、引用、可变与不可变类型、函数注意事项】
- HTML rel 属性
- 一号团队-团队任务3:每日立会(2018-12-01)
- 怎么查看电脑有没有python_python人工智能爬虫系列:怎么查看python版本_电脑计算机编程入门教程自学...
- BZOJ 1013: [JSOI2008]球形空间产生器sphere( 高斯消元 )
- 详解3种常用数据分析方法,满足你职场95%的高频需求
- Pytorch nn.Fold()的简单理解与用法
- Lnixu Bash
- torch安装及CUDA和torch版本的对应关系
- 使用JDK自带jvisualvm监控tomcat(收藏)
- 数据--第23课 - 队列的优化实现
- Centos系统设置
- java 遍历 Map 的六种方式 学习笔记
- matlab画倾斜的椭球_用matlab绘制椭球体x^2/4+y^2/9+z^2/16=1,并通过改变观察点获得它在各个坐标面上的投影。...
- emacs下使用google-cpplint
- 你承认电子计算机是天之骄子改为双重否,双重否定句练习题
- android 各种服务介绍,Android 网络服务介绍