C语言的艺术:强大的宏

原创 2016年05月31日 00:06:01

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

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

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

#define sqrt(x) x*x
sqrt(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) # x
printf(String(""""""));

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

#define Link(a,b) a ## b
int 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语言。
我是算法吹,以后会给大家带来更多精彩的算法。这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

C语言宏中"#"和"##"的用法

C语言宏中"#"和"##"的用法   在查看linux内核源码的过程中,遇到了许多宏,这里面有许多都涉及到"#"和"##",因此,在网上搜索了一些资料,整理如下:   一、一般用法 我们...

C/C++-技巧-宏

一、宏基础 宏在c/c++中扮演者比较重要的角色,虽然难以阅读和调试的缺点让宏的使用饱受诟病,但是在一些特殊的情况下,使用宏会带来极大的方便,甚至可以实现一些用其他方式无法实现的功能。 在c/c++程...

C语言的宏总结

1. 简单宏定义 简单的宏定义有如下格式: [#define指令(简单的宏)]  #define  标识符替换列表 替换列表是一系列的C语言记号,包括标识符、关键字、数、...
  • PirLCK
  • PirLCK
  • 2016-04-26 21:50
  • 3022

c语言宏定义

一. #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时...

详解C语言中的宏定义

1. 防止一个头文件被重复包含  [cpp] view plain copy  print? #ifndef COMDEF_H    #define C...

失落的C语言结构体封装艺术

目录 1. 谁该阅读这篇文章 2. 我为什么写这篇文章 3.对齐要求 4.填充 5.结构体对齐及填充 6.结构体重排序 7.难以处理的标量的情况 8.可读性和缓存局部性 9.其他封装的...

C语言循环的小艺术

1. 质数判断 对于这个,很多人可能会直接这样写: view plaincopy to clipboardprint? int isPrime(int n) ...

C语言循环的小艺术

http://blog.csdn.net/csdn_zc/article/details/6776929 1. 质数判断 对于这个,很多人可能会直接这样写: ...

C语言循环的小艺术(转)

原地址:http://blog.csdn.net/csdn_zc/article/details/6776929   1. 质数判断 对于这个,很多人可能会直接这样写: ...
  • hit97
  • hit97
  • 2011-09-16 13:27
  • 329

C语言的艺术之——标识符命令与定义

好记性不如烂笔头o(^▽^)o系列的文章: 《C语言的艺术之——头文件》 《C语言的艺术之——函数》 C语言的艺术之——标识符命令与定义 C语言的艺术之——变量、常量和宏 C语言的艺术之——...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)