所谓的宏就是一种预处理命令,什么是与处理呢?即在编译过程之前先对程序代码做出的必要的转换处理。宏有两个作用:

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语言之宏的更多相关文章

  1. C语言中宏定义(#define)时do{}while(0)的价值(转)

    C语言中宏定义(#define)时do{}while(0)的价值 最近在新公司的代码中发现到处用到do{...}while(0),google了一下,发现Stack Overflow上早有很多讨论,总 ...

  2. c 语言中宏定义和定义全局变量的区别

    宏定义和定义全局变量的区别: 1 作用时间不同. 宏定义在编译期间即会使用并替换,而全局变量要到运行时才可以. 2 本质类型不同. 宏定义的只是一段字符,在编译的时候被替换到引用的位置.在运行中是没有 ...

  3. 注解是建立在class文件基础上的东西,同C语言的宏有异曲同工的效果

    注解是建立在class文件基础上的东西,同C语言的宏有异曲同工的效果 https://www.cnblogs.com/deman/p/5519901.html @是java注解,即annotation ...

  4. C语言的宏macro的使用

    C's Macro Introduction 1.The Connect Macros: ## 这是一个预处理连接符,这个操作符主要用来将两个符号连接成为一个完整的宏符号.通过下面的代码,可以看到其具 ...

  5. C语言基础--宏

    宏在C语言中经常使用,在linux的源码中可以看到很多宏的高级应用.因此不理解宏,就很难理解代码.本文参考一些互联网资料做一些总结,希望给大家带来帮助. 先说说使用宏的优点及缺点: 优点: 1.提高代 ...

  6. 【编程基础】C语言常见宏定义

    我们在使用C语言编写程序的时候,常常会使用到宏定义以及宏编译指令,有的可能比较常用,有的可能并不是很常用,是不是所有的C语言宏定义以及宏指令你都清楚呢? 指令 用途详细介绍 # 空指令,无任何效果 # ...

  7. C语言在宏定义中使用语句表达式和预处理器运算符

    语句表达式的亮点在于定义复杂功能的宏.使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞.下面以一个简单的最小值的宏为例子一步步说明. 1.灰常简单的么,使用条件运算符 ...

  8. C语言程序设计--宏和预处理

    C语言宏 宏定义常量 #include <stdio.h> #define SIZE 100 #define BANNER "WARNING:" int main(vo ...

  9. C语言高级宏技巧

    特殊符号#.## (1)# When you put a # before an argument in a preprocessor  macro, the preprocessor turns t ...

随机推荐

  1. 使用命令行编译、打包、运行WordCount--不用eclipse

    1)首先创建WordCount1023文件夹,然后在此目录下使用编辑器,例如vim编写WordCount源文件,并保存为WordCount.java文件 /** * Licensed under th ...

  2. js中几个正则表达式相关函数使用时g标志的作用

    首先,javascript中涉及到正则表达式的函数总共有6个,可分为两种: 1.第一种是作为字符串对象的方法,即以 String.fun(); 形式调用,这里包括 split.search.match ...

  3. BestCoder 1st Anniversary($) 1003 Sequence

    题目传送门 /* 官方题解: 这个题看上去是一个贪心, 但是这个贪心显然是错的. 事实上这道题目很简单, 先判断1个是否可以, 然后判断2个是否可以. 之后找到最小的k(k>2), 使得(m-k ...

  4. java web多线程

    1.多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线 程的处理的数据,而B线程又修改了A线程处理的数理.显然这是由于全局资源造成的,有时为了解 决此问题,优先考虑 ...

  5. linux下ping加时间戳实时输出到文件 放后台运行

    放后台运行命令:setsid 实时输出命令:unbuffer 加时间戳:awk '{ print $0"\t" strftime("%D_%H:%M:%S",s ...

  6. JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-007UserTypes的用法(@org.hibernate.annotations.Type、@org.hibernate.annotations.TypeDefs、CompositeUserType、DynamicParameterizedType、、、)

    一.结构 二.Hibernate支持的UserTypes接口  UserType —You can transform values by interacting with the plain JD ...

  7. 世界上还有一个东西叫Virtual Pascal

    官网是:http://web.archive.org/web/20060312064321/http://www.vpascal.com/news.php?item.16 不过2005年就不再维护了. ...

  8. WPF之无法触发KeyDown或者KeyUp键盘事件

    有时候我们可能在Panel(StackPanel.Canvas.Grid)上或者是在一些默认不支持Focus的控件上添加了KeyDown或者KeyUp,可是残酷的现实告诉我们,这是无法触发的,怎么办呢 ...

  9. Android 音乐播放器之--错误状态下调用导致的异常

    MediaPlayer必须在合适的状态下调用合适的方法,否则会出现异常,下面列出常见错误信息和说明: 1.E/MediaPlayer(11310): stop called in state 1 调用 ...

  10. Android 使用Application类保存应用的全局数据

    在实际应用我们经常需要对数据进行交互与保存,但Intent中默认的方法对传输数据是有类型限制的,当我们需要传输或保存一个复杂的泛型数据时,使用Application是一个很好的解决办法. 顾名思义,A ...