C语言之宏
所谓的宏就是一种预处理命令,什么是与处理呢?即在编译过程之前先对程序代码做出的必要的转换处理。宏有两个作用:
1.当遇到需要将程序某个特定的数量在程序中出现的所有实例通通加以修改时,程序只需改动一处即可。
2.大多数C语言函数调用时都会带来重大的系统开销,而宏看上去像一个函数却没有函数调用的开销。
宏虽然有优点,但是也有其自身的缺陷。
首先得明确宏只是简单地替换。例如
#define CHAR char*
Int main()
{
Char a=’A’;
CHAR p1,p2;
P1=&a;
P2=&a;//错误
}
在运行如上程序代码的时候程序会报错,什么原因呢?由于宏只是简单地替换,所以CHAR p1,p2;代表的是char *p1,p2;只有p1被定义成了指针,而p2只是字符类型的普通变量。
这样的简单替换往往会导致另一种错误出现:
#define add(a,b) a + b
result = add (1,2) * add(3,4);
如上的定义的本意是先计算1与2,3与4之和,然后把他们的结果乘起来。但是运行的结果却不是我们想象的那样,编译器会把表达式简单替换为:1+2*3+4,这样我们就可以看出问题的所在了。要解决这种问题的出现,就是不要吝啬括号的使用。应针对每个宏参数和整个表达式都加上括号,避免歧义的产生。
其次,不能忽略宏定义中的空格。例如:
#define f (x) ((x) * 2)
这种定义有两种可能的解释方式:
① 定义了宏 f, 其中f代表(x) ((x) * 2)
② 定义了宏 f (x),其中x是宏参数,f (x)宏整体代表((x) * 2)
这两种定义只能是第一种,因为在宏定义时中间不能有空格。有了空格首先会对宏的理解产生歧义性,其次在编译时会产生意想不到的结果。因此应该避免这种情况的发生。
以上的规则只适用于宏的定义,在宏调用的时候中间有没有空格都无所谓。即f(5)与
f (5)都是一样的。
最后,我们要关注的是宏的副作用。
宏看起来像函数,但它并不是函数。我们先看如下的代码:
#include<stdio.h>
#define max(a,b) a>b?a:b
int main()
{
char ar[3] = { 2, 3, 1 };
int i = 1; int biggest = ar[0];
while (i < 3)
{
biggest = max(biggest, ar[i++]);
}
printf("max value=%d\n", biggest);
return 0;
}
这段代码的本意是求出数组中最大的值,但是经过运行我们发现,运行的结果并不是我们所想象的3,而是1,这个结果明显错误,但是错在哪呢?我们先将上述的式子中的max进行宏替换:
biggest=biggest>ar[i++]?biggest:ar[i++];
首先,变量biggest与ar[i++]进行比较,此时i的值为1,ar[1]的值为3,而此时biggest的值为ar[0]为2,因此表达式的值为假。这里由于i++的副作用,比较后i递增为2.
因为关系运算的结果为假,所以ar[i++]的值被赋给biggest,然而此时i的值为2.所以实际赋给变量的值是ar[2]即1,这时,又因为i++的副作用,i的值变成3.因此也就不难理解为什么程序运行结果不是3而是1了。
要避免宏的副作用,这里有两个方法:
1.把i++提出来,即将原来的biggest=max(biggest,ar[i++]);
改为:biggest=max(biggest,ar[i]);
i++;
2.将宏改为函数
C语言之宏的更多相关文章
- C语言中宏定义(#define)时do{}while(0)的价值(转)
C语言中宏定义(#define)时do{}while(0)的价值 最近在新公司的代码中发现到处用到do{...}while(0),google了一下,发现Stack Overflow上早有很多讨论,总 ...
- c 语言中宏定义和定义全局变量的区别
宏定义和定义全局变量的区别: 1 作用时间不同. 宏定义在编译期间即会使用并替换,而全局变量要到运行时才可以. 2 本质类型不同. 宏定义的只是一段字符,在编译的时候被替换到引用的位置.在运行中是没有 ...
- 注解是建立在class文件基础上的东西,同C语言的宏有异曲同工的效果
注解是建立在class文件基础上的东西,同C语言的宏有异曲同工的效果 https://www.cnblogs.com/deman/p/5519901.html @是java注解,即annotation ...
- C语言的宏macro的使用
C's Macro Introduction 1.The Connect Macros: ## 这是一个预处理连接符,这个操作符主要用来将两个符号连接成为一个完整的宏符号.通过下面的代码,可以看到其具 ...
- C语言基础--宏
宏在C语言中经常使用,在linux的源码中可以看到很多宏的高级应用.因此不理解宏,就很难理解代码.本文参考一些互联网资料做一些总结,希望给大家带来帮助. 先说说使用宏的优点及缺点: 优点: 1.提高代 ...
- 【编程基础】C语言常见宏定义
我们在使用C语言编写程序的时候,常常会使用到宏定义以及宏编译指令,有的可能比较常用,有的可能并不是很常用,是不是所有的C语言宏定义以及宏指令你都清楚呢? 指令 用途详细介绍 # 空指令,无任何效果 # ...
- C语言在宏定义中使用语句表达式和预处理器运算符
语句表达式的亮点在于定义复杂功能的宏.使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞.下面以一个简单的最小值的宏为例子一步步说明. 1.灰常简单的么,使用条件运算符 ...
- C语言程序设计--宏和预处理
C语言宏 宏定义常量 #include <stdio.h> #define SIZE 100 #define BANNER "WARNING:" int main(vo ...
- C语言高级宏技巧
特殊符号#.## (1)# When you put a # before an argument in a preprocessor macro, the preprocessor turns t ...
随机推荐
- POJ 2541 Binary Witch(逆序KMP,好题)
逆序KMP,真的是强大! 参考链接,下面有题意解释:http://blog.sina.com.cn/s/blog_6ec5c2d00100tphp.htmlhttp://blog.csdn.net/s ...
- JS正则汇总
1.基本定义: \s:用于匹配单个空格符,包括tab键和换行符; \S:用于匹配除单个空格符之外的所有字符; \d:用于匹配从0到9的数字; \w:用于匹配字母,数字或下划线字符; \W:用于匹配所有 ...
- java 时间戳与日期字符串相互转换
/** * 时间戳转换成日期格式字符串 * @param seconds 精确到秒的字符串 * @param formatStr * @return */ public static String t ...
- 自己的gitignore文件
*.bak*.txt*.vm.gitignore#svn.svn/# built application files*.apk*.ap_ # files for the dex VM*.dex # J ...
- AssemblyInfo.cs文件参数具体讲解
在asp.net中有一个配置文件AssemblyInfo.cs主要用来设定生成的有关程序集的常规信息dll文件的一些参数,下面是默认的AssemblyInfo.cs文件的内容具体介绍 //是否符合公共 ...
- C#DataGridView 美化
private void dataGridView(DataGridView dataGridView) { System.Windows.Forms.DataGridViewCellStyle da ...
- hdu 1124 Factorial(数论)
题意: 求n!的尾0的个数 分析: 0一定是由因子2和5相乘产生的: 2的个数显然大于5的个数,故只需统计因子5的个数 n/5不能完全表示n!中5的个数(egg: 25),应该n/=5后,累加上n/2 ...
- C++标准文档下载
C++真正正式公布的标准只有三个:C++98.C++03.C++11. C++98是第一个正式的C++标准, C++03是在C++98上面进行了小幅度的修订, C++11则是一次全面的大进化(之前称C ...
- VC高手们的博客
http://www.cnblogs.com/killmyday/tag/Debug/ (关于符号调试等内容比较多)
- Android笔记——Handler更新UI示例
public class MainActivity extends ActionBarActivity { private TextView textView; private int i=0; @O ...