一个很奇怪的文章,宏定义需要特别注意吗?

答案是:是的!

宏定义是C语言提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译。

宏定义和操作符的区别是:宏定义是替换,不做计算,也不做表达式求解。

这篇文章会谈到一些宏定义,除了我们常用的 #define 之外,还会有一些高级用法。

首先我们来谈谈#define…我们知道,它配合 #if、#endif、#ifdef、#ifndef可以实现文件包含、条件编译、宏定义等功能,但是这并不是我们这里要讲的重点,因为它…太简单。

不过还是简单提一下吧:

首先是宏定义最简单的用法:

把程序中出现的Pi全部换成3.1415926

note 请注意:
(1)宏名一般用大写
(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号;
(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
(6)可以用 #undef 命令终止宏定义的作用域
(7)宏定义允许嵌套
(8)字符串( ” ” )中永远不包含宏
(9)宏定义不分配内存,变量定义分配内存。
(10)宏定义不存在类型问题,它的参数也是无类型的。
除了一般的字符串替换,还可以做参数代换
格式:
#define宏名(参数表) 字符串
例如:#define S(a,b) a*b
area=S(3,2);第一步被换为area=a*b; ,第二步被换为area=3*2;
类似于函数调用,有一个哑实结合的过程:

PS.所谓哑实结合我的理解是从形参到实参的转变。

warning  警惕这里!
(1)实参如果是表达式容易出问题

area=S(a+b);第一步换为area=r*r;,第二步被换为area=a+b*a+b;
正确的宏定义是#define S(r) ((r)*(r))
(2)宏名和参数的括号间不能有空格
(3)宏替换只作替换,不做计算,不做表达式求解
(4)函数调用在编译后程序运行时进行,并且分配内存。宏替换在编译前进行,不分配内存
(5)宏的哑实结合不存在类型,也没有类型转换。
(6)宏展开使源程序变长,函数调用不会
(7)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)

 

同时要注意 #define的 几个冷门知识:
比如下面几个知识点你会吗?可以看下:

(1)#define NAME “zhangyuncong”
程序中有”NAME”则,它会不会被替换呢?

(2)#define 0x abcd
可以吗?也就是说,可不可以用不是标识符的字母替换成别的东西?

(3)#define NAME “zhang
这个可以吗?

(4)#define NAME “zhangyuncong”
程序中有上面的宏定义,并且,程序里有句:
NAMELIST 这样,会不会被替换成 “zhangyuncong”LIST

大多数这个其实编程一试即可…但是还是要稍加注意。

第一个,””内的东西不会被宏替换。这一点应该大家都知道。
第二个,宏定义前面的那个必须是合法的用户标识符
第三个,宏定义也不是说后面东西随便写,不能把字符串的两个””拆开。
第四个:只替换标识符,不替换别的东西。NAMELIST 整体是个标识符,而没有 NAME 标识符,所以不替换。
也就是说,这种情况下记住:#define 第一位置 第二位置
(1) 不替换程序中字符串里的东西。
(2) 第一位置只能是合法的标识符(可以是关键字)
(3) 第二位置如果有字符串,必须把 “” 配对。
(4) 只替换与第一位置完全相同的标识符
还有就是老生常谈的话:记住这是简单的替换而已,不要在中间计算结果,一定要全部替换出表达式之后再算。

带参宏一般用法
比如 #define MAX(a,b) ((a)>(b)?(a):(b))
则遇到MAX(1+2,value)则会把它替换成:
((1+2) > (value) ? (1+2) : (value))
注意事项和无参宏差不多。

但还是应注意以下的替换规则:
#define FUN(a) “a”
则,输入FUN(345)会被替换成什么?
其实,如果这么写,无论宏的实参是什么,都不会影响其被替换成 “a” 的命运。
也就是说,”” 内的字符不被当成形参,即使它和形参一模一样。

 

那么问题来了,如果按照上文的替换方法,我要替换一个字符串怎么办呢。

那就涉及到了 宏定义中 #的用法:

先举个板栗子~:

在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组

简化理解:#是“字符串化”的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串

ERROR_LOG(“add”); 转换为 fprintf(stderr,”error: “add”\n”);
ERROR_LOG(devied =0); 转换为 fprintf(stderr,”error: devied=0\n”);

接下来我们看看理论的东西:

#define STR(str) #str
后一个#用于把宏定义中的参数两端加上字符串的””
比如,这里 STR(my#name) 会被替换成 “my#name”
一般由任意字符都可以做形参,但以下情况会出错:
STR())这样,编译器不会把“)”当成STR()的参数。
STR(,)同上,编译器不会把“,”当成STR的参数。
STR(A,B)如果实参过多,则编译器会把多余的参数舍去。
STR((A,B))会被解读为实参为:(A,B),而不是被解读为两个实参

 

同样的,除了#的用法之外,还有一个用法是 ##…之前不认真看看还真不知道,C语言的#define还有这么多门道!

“##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。

在普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。

但是这样做的结果是,被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些##来替代空格。

TYPE1(int, c); 转换为:int name_int_type ;
(因为##号将后面分为 name_ 、type 、 _type三组,替换后强制连接)

TYPE2(int, d);转换为: int d_int_type ;
(因为##号将后面分为 name、_、type 、_type四组,替换后强制连接)

再来一个简化版的:

则会将形参str的前面加上L
比如:WIDE(“abc”)就会被替换成L”abc”
如果有#defineFUN(a,b) vo##a##b()
那么FUN(id ma,in)会被替换成void main()

【C++】C/C++中的宏定义
Tagged on:
0 0 投票数
Article Rating
订阅评论
提醒

0 评论
内联反馈
查看所有评论