C语言的艺术:强大的宏

来源:互联网 发布:java web项目 log4j 编辑:程序博客网 时间:2024/04/28 09:23

这次不讲算法了,讲一讲C语言里面一个很强大的功能:
宏,是一个大家都很熟悉的概念,很多人也经常使用宏,但是用的都不怎么深,我所知道的最常见的用法就是用宏抽象数组长度。

#define N 99a[N] = {0};

实际上,宏就是简单的替换,编译器在编译程序时,遇到N,会将其替换成99。
那么下面来看这段代码:

#define sqrt(x) x*xsqrt(5);sqrt(3+2);

这里就出现了问题,sqrt(5)的结果是25没错,但是sqrt(3+2)的结果错误,为什么呢?注意,前面说过,宏是简单替换sqrt(3+2)替换后变成3+2*3+2,所以结果是11,想要结果正确,需要这样定义宏:

#define sqrt(x) (x)*(x)

这样就没问题了。
当然,宏很强大,还有很多其他的功能。比方说转换字符串,在讲这个知识点前,我想问大家,我们该如何创建一个如:”“”“”“”“”“”“”;全是引号的字符串?很容易可以想到使用转义,但是转义是不是太麻烦了?所以下面介绍一种新方法:

#define String(x) # xprintf(String(""""""));

这个宏的作用可以简单理解为将String()括号里的字符串原样输出,但是在使用时,如果引号不匹配,会导致报错,而且它还会有意想不到的结果。后面我会再次提到这个。
上面讲了#号,很多人应该听说过,还有##这个功能吧。其实##很简单。

#define Link(a,b) a ## bint Link(a,i);

a ## b的作用就是将 a 和 b 连接起来,组合成ab,那么上面的程序
int Link(a,i);就等价于int ai;
这是个很有用的功能,但是大家千万不要乱用,这个功能单单放在C语言里是没有什么用的,有人写下面的代码:

int i = 0;int Link(a,i);++i;int Link(a,i);++i;int Link(a,i);++i;int Link(a,i);

看上去这样好像是对的,但是并不是你想象的那样,并没有定义a0,a1,a2,a3;而是定义了四次ai;所以程序会报错。再说一遍,宏只是简单替换,替换的背后没有任何逻辑。

下面再来看一个输出的用法:

#define Write(String) printf(#String)Write(Liuruiyang);

这个宏定义里面使用了#的功能,将Liuruiyang这串字转换成字符串。
但是,上面的这段宏是有BUG的。比如:

int x = 0, y = 1;Write("x is %d and y is %d", x, y);

你可以试一下,结果是报错
macro “Write” passed 3 arguments, but takes just 1
Write(“x is %d and y is %d”, x, y);
说参数不够,呵呵,那我给你3个参数不就行了。

#define Write(String,a,b) printf(#String,a,b)

这一改,就对了。但是这样输出两个参数就不行了。

Write("x is %d and y is %d", x);

可以看出,宏也是严格控制参数的,那么如何让这个宏只读取两个参数呢?

Write("x is %d and y is %d", x,);

这种写法仅仅比上面的写法多了一个逗号,这样依旧报错,但是报错不在宏定义上,而是定向在了printf();函数上,因为printf();传入了一个错误参数。

下面讲最后一个知识点:
我们使用宏的时候,有时并不知道到底要传进来多少参数,这时,可以按下面的方法写:

#define Write(...) printf(#__VA_ARGS__)int x = 0, y = 1;Write("x is %d and y is %d", x, y);

运行一下,看到结果又要呵呵了。
“x is 4201104 and y is 2686868”, x, y
当然,对这个运行结果我就不多说了,认真看的人一眼就能看出错在哪。
下面说一下这个可变参数:
直接看是看不出来什么的,还是改一改运行一下吧。

#define Write(...,a) printf(#__VA_ARGS__)

改成这样是报错的,跟函数一样,函数也有可变参数,可变参数必须位于所有参数的末尾,那就再改吧。

#define Write(a,...) printf(#__VA_ARGS__)

这样的话就直接输出为 x, y 了。
看不出来什么?那再改改。

#define Write(a,b,...) printf(#__VA_ARGS__)

运行结果变成了 y 。
这三种宏,使得结果从”x is 4201104 and y is 2686868”, x, y变为x, y再变为y。
那么还可以再改成这样:

#define Write(a,b,c,...) printf(#__VA_ARGS__)

想必大家已经知道结果了。
很明显,每次截取了一个逗号,这有点像那些动态语言里面的列表解析这个概念了,或者说那些动态语言的列表解析就是这样实现的,Python就是用C语言写的,有没有想要去看Python源码的冲动呢,还有列表解析究竟是干什么用的呢?当然,这些都是题外话了,我可不希望有人看到了Python,试了试,发现Python很简单,就放下C语言而跑去学Python。我也不是说Python不好,我只是觉得与C语言相比,Python更适合去做一些工作周期短的开发,如果你的目的是学习,那就不要犹豫,当然选C语言。
我是算法吹,以后会给大家带来更多精彩的算法。这里写图片描述

1 0
原创粉丝点击