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 ...
随机推荐
- socket异步通信-如何设置成非阻塞模式、非阻塞模式下判断connect成功(失败)、判断recv/recvfrom成功(失败)、判断send/sendto
socket异步通信-如何设置成非阻塞模式.非阻塞模式下判断connect成功(失败).判断recv/recvfrom成功(失败).判断send/sendto 博客分类: Linux Socket s ...
- vi/vim使用指北 ---- Moving Around in a Hurry
上一篇文章中,简单列出了一些基本的Vim操作,也列出了很多的光标移动命令,本章主要是有哪些命令可以更快的移动光标. vim的编辑操作,用得最多就是移动光标,对于很少行的文件来说,基本的命令就够用了,但 ...
- 如何说服你的老板必须使用APM?
APM研究院 2015/04/24 16:56 2013年,某权威机构提供一组数据显示:亚马逊每100毫秒延迟会使销售额下降1%:雅虎一秒钟服务器延迟导致收入下降2.8%:谷歌搜索结果页面放缓100毫 ...
- HDU4945 2048(dp)
先是看错题意..然后知道题意之后写了发dp..无限TLE..实在是不知道怎么优化了,跑了遍数据是对的,就当作理论AC掉好了.. #pragma warning(disable:4996) #inclu ...
- MySQL: InnoDB 还是 MyISAM? (转载)
MyISAM存储引擎 原文作者:http://www.cnblogs.com/villion/archive/2009/07/09/1893762.html MyISAM是 默认存储引擎.它基于更老的 ...
- group_concat
分割字符串 group_concat(a.EvidencesID separator ',') as EvidencesID #在MySQL配置文件(my.ini)中默认无该配置项,使用默认值时,值为 ...
- (10)nehe教程4--旋转
旋转: 在这一课里,我将教会你如何旋转三角形和四边形.左图中的三角形沿Y轴旋转,四边形沿着X轴旋转. 上一课中我教给您三角形和四边形的着色.这一课我将教您如何将这些彩色对象绕着坐标轴旋转.其实只需在上 ...
- C#枚举硬件设备(升级版)
原文:C#枚举硬件设备(升级版) 先取设备类型: ; } }
- MongoDB小记
mongodb的一个简单使用. package com.chuntent.mongo; import java.util.Map; import java.util.Map.Entry; import ...
- Docker仓库搭建(Registry + Portus)
1.更新系统: yum update -y 2. 安装docker-compos yum -y install epel-release #pip安装包在epel源中 yum -y inst ...