C语言宏操作之反复斟酌

来源:互联网 发布:重庆软件学院18 编辑:程序博客网 时间:2024/05/08 10:29

一般我们在程序中使用宏主要是一下几种情况:

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!

是的,要得到一只活泼可爱、萌萌哒的宏真的不容易,且行且珍惜吧(*^-^*)

 

 

 

 

 

0 0
原创粉丝点击