一般我们在程序中使用宏主要是一下几种情况:
1. 有些变量在程序的多处出现,我们希望统一进行管理。
2. 定义一些类型简称,在使用时简化代码。
3. 将一些简单的操作定义为宏,从而避免每次调用函数操作时造成系统效率下降。例如C中的getchar()、putchar(),就经常会被实现为宏。
虽然宏非常有用,但是宏只是对程序的文本起作用。也就是说,宏提供了一种对组成C程序的字符进行变换的方式,而并不作用于程序中的对象。因而,宏既可以使一段看上去完全不合语法的代码成为一个有效的C程序,也能使一段看上去无害的代码运行出出乎意料的结果。
对于第一种情况,例如:
#define MAX 10000
在预编译时,我们代码中出现的MAX都会被替换为文本10000(其实在编译时,代码已经不再是我们看上去的样子了,预编译器会给我们做一些事情)。这种情况下,只要注意使用时的上下文,一般不会有太大的问题。
记得自己初学C语言的时候觉得很奇怪,不是应该所有语句都以分号结尾吗?为什么宏定义就可以搞“特殊化”,那些资本主义国家连创造的编程语言都带有一些“个人英雄主义”色彩。其实宏定义后面加分号也是可以的,只是你要清楚地知道:如果在定义时有分号,那么在宏展开时也会在后面加上分号。正如上面所说,宏定义只是一种文本变换方式。
例如,你定义了一个如下的宏(其实这属于第三种情况,但是很简单):
#define f(x) x + 1;
那么,在调用的时候只要这样写就可以了: f(3) 而它的后面不需要加分号,因为宏展开时已经加上了。
对于第二种情况,typedef可能会更合适用于处理这种类型名称的再命名。如下:
#define T1 struct stu *
typedef struct stu * T2;
如果我们的程序中有如下语句:
T1 a,b; //a: struct stu * b:struct stu
T2 a,b; //a: struct stu * b:struct stu *
要注意,T1中b其实是结构体类型,而不是结构体指针类型。
第三种情况可能会稍微复杂一些。例如,我们定义一个求两个数最大值的宏。首先想到的可能是下面这样的表达式:
#define max(a,b) a>b?a:b
这个式子是正确的吗?sometimes!
如果我们的调用是:max(1&&3,2) 其结果刚好与期望的结果相反。我们本该得到2,最后的结果却是1 。因为展开后是这样的:1&&3>2 ? 1&&3 :2(关系操作符的优先级>逻辑运算符>条件运算符?: )。
我们可以做如下改进解决优先级的问题:
#define max(a,b) (a)>(b)?(a) : (b)
此时再代入上式就没问题了。但是这样就够了吗?再看如下调用:
A = max(3,2) + 1;
此时得到的结果是3,而按程序的正确逻辑应该是4。它展开后成了这样:
(3)>(2)?(3): (2) + 1;注意:+1成了false时才出现的表达式!
鉴于此,我们可以做如下的更改:
#define max(a,b) ((a)>(b)?(a) : (b))
这时可能你已经把悬着的心终于放下来了,喵了个咪,终于解决了!然而事实并非如此(づ。◕‿‿◕。)づ考虑一下下面的情况:
int i = 2;
int XXX = max(i++,1);
按字面逻辑,我们期望的结果该是2,而事实是3。你可能会说,我就是想得到3的啊(少侠,好jian法)。此时的展开时这样的: ((i++)>(1)?( i++) : (1)),也就是说i++被计算了两次。该肿么办?问题的关键就是多次计算,那么如何去掉多次计算呢?两种方案:一是实现为函数;二是用中间变量来保存原来的值。
下面是第二种解决方案:
static int temp1, temp2; //用于保存中间值的变量
#define max(a,b) (temp1 = (a), temp2 =( b), temp1>temp2 ? temp1 : temp2)
此时终于得到了我们所期望的2!
是的,要得到一只活泼可爱、萌萌哒的宏真的不容易,且行且珍惜吧(*^-^*)