内容简介:C——宏 VS 函数
想要了解宏和函数,就得先介绍下一个.c代码是如何到最后的可执行文件的,主要经过了以下几个阶段:
①预处理器处理过程中又可以分为:1>删除注释;2>头文件展开(将所包含的头文件内容全部复制到此文件中);3>宏替换;4>条件编译
②编译过程可以分为: 1>词法分析;2>语义分析;3>符号汇总;4>语法分析
③汇编过程则是将形成的汇编代码转换成二进制代码,同时形成对应的符号表。
先分别简单介绍下函数和宏:
①#define宏:把参数替换到文本中,通常称为宏或者定义宏;
②函数:它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。每次使用时只需要调用即可。
接下来总结下宏和函数各自在不同分析角度下的情况:
1>代码长度:
#define宏:使用了几次宏,相对应的宏代码就会被插入到代码中几次,所以如果你的宏不是很小,并且使用次数也不少,那么整体的代码长度就会大幅增加;
函数:函数代码只需要写一份,代码在执行时每次遇到函数调用那么就去函数内部执行一次,并不会大幅增加代码程度、
2>执行速度:
#define宏:由于在代码 在预编译时就已经将参数替换了进去,执行速度很快;
函数:函数涉及到传参问题以及调用函数并返回的过程,会增加执行时间 。
3>操作符优先级:
#define宏: 这就是一个大坑,千万不要想着省你的括号!!! 稍后看例1就会知道了。如果你的括号不够多,那么很可能会出现你意料之外的运行结果;
函数:表达式的求值结果更容易预测。
4>参数求值:
#define宏 :参数每次用于宏定义时,都会被重新求值,所以具有副作用的参数就又会产生你意料不到的结果, 这也是一个坑!!!! 一会看例2就知道了;
函数:参数在调用前只求值一次,不会导致多种求值问题,参数副作用也不会造成任何问题。
5>参数类型
#define宏:宏与类型无关,只要参数操作合法,便可以适用于任何参数类型;
函数:函数在传参时就规定了类型,不能随意使用。
现在就到了跳坑的时候(如果你基础不是很好,那么你一定会掉进去):
例1:
#define _CRT_SECURE_NO_WARNINGS 1 #define SQUARE(X) X * X #include <stdio.h> #include <Windows.h> int main() { int x = 5; printf("%d\n", SQUARE(x+1)); printf("%d\n", 10 * SQUARE(x+1)); system("pause"); return 0; }
可以先自己按照自己的理解推断下输出结果:
先来分析第一个printf:如果你认为会打印出的结果是36那恭喜你成功的掉坑里了。宏是把参数替换到文本中!一定要注意是替换!!所以该语句可以等价为
printf("%d\n", x + 1 * x + 1); 这样一看结果很显而易见是11
如果你想输出6 *6 那就需要将宏改为(X)*(X),这样经过预处理之后就等价于
printf("%d\n",(x+1)*(x+1)); 结果也就变成了6 * 6
经过了第一个printf,第二个如果你认为结果为10*5+1*5+1 = 56那就说明你理解了替换的意思了,否则还需要再练练。第一个坑到这就暂时告一段落
例2:
#define _CRT_SECURE_NO_WARNINGS 1 #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) #include <stdio.h> #include <Windows.h> int main() { int x = 5; int y = 8; int z = MAX(x++, y++); printf("x=%d, y=%d, z=%d", x, y, z); system("pause"); return 0; }
我们定义了一个宏用来求a,b两个参数中的较大值,这时候在参数替换后,整个z的赋值语句等价于:
int z = ( (x++) > (y++) ) ? (x++) : (y++) );
所以在进行比较时是用的++前的值,也就是5和8进行比,在判断完这个的同时,5和8同时完成后增操作变成6和9,三目运算符如果条件成立则运算冒号前的表达式,冒号后的表达式不运算,显然此次判断运算y++,y就变成了10;
最后的printf打印出:x=6, y=10, z=9;这个时候就出现了我们所谓的副作用,我们并不想将y自增两次。
但是用函数就可以完成我们想要完成的事情:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <Windows.h> int Max(int x, int y) { return x > y ? x: y; } int main() { int x = 5; int y = 8; int z = Max(x++, y++); printf("x=%d, y=%d, z=%d", x, y, z); // x=6,y=9,z=8 system("pause"); return 0; }
主要区别就在于我们上面所提到的参数在函数被调用前只求值一次,在函数中多次使用参数并不会导致多种求值过程,而#define宏定义则会在某些情况下产生意想不到的副作用。所以我们在一般使用函数或宏定义实现某个功能时,可以在不同的情况下考虑用不同的方法,前提是一定要有能掌握它的能力,否则到时候又会让你多出一些BUG。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Python 拓展之特殊函数(lambda 函数,map 函数,filter 函数,reduce 函数)
- Python 函数调用&定义函数&函数参数
- python基础教程:函数,函数,函数,重要的事说三遍
- C++函数中那些不可以被声明为虚函数的函数
- 017.Python函数匿名函数
- 纯函数:函数式编程入门
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JavaScript & jQuery交互式Web前端开发
[美]达克特(Duckett,J.) / 杜伟、柴晓伟、涂曙光 / 清华大学出版社 / 2015-6-9 / 79.80元
欢迎选择一种更高效的学习JavaScript和jQuery的方式。 你是一名JavaScript新手?或是您曾经向自己的Web页面上添加过一些脚本,但想以一种更好的方式来实现它们?本书非常适合您。本书不仅向您展示如何阅读和编写JavaScript代码,同时还会以一种简单且视觉化的方式,教您有关计算机编程的基础知识。阅读本书之前,您只需要对HTML和CSS有一些了解即可。 通过将编程理论......一起来看看 《JavaScript & jQuery交互式Web前端开发》 这本书的介绍吧!