宏在编译之前,需要进行预处理,将宏直接提换成宏定义的代码,是直接替换,也就是说,在预处理之后,你再看代码,发现宏定义已经被替换过来了,你看到是你定义之后的那一串代码。
专业从事企业网站建设和网站设计服务,包括网站建设、域名注册、雅安服务器托管、企业邮箱、微信公众号开发、微信支付宝成都微信小程序、app软件开发公司、软件开发、等服务。公司始终通过不懈的努力和以更高的目标来要求自己,在不断完善自身管理模式和提高技术研发能力的同时,大力倡导推行新经济品牌战略,促进互联网事业的发展。
而函数,在编译之后,有一系列调用函数的过程,比如,传参,压栈等,这部分是编译器所做的。
C 语言编译链接过程:
test.c(原始代码) -- 预处理 -- test.i(经过预处理的)-- 编译 -- test.s(汇编代码)-- 汇编 -- test.o(目标文件,其实这部分已经是单个文件的完整二进制文件了,只是还不能执行,如果不懂这句话,可以再问我,其实这部分知识,平时也很少遇到) -- 链接 -- test (可执行文件,比如一个程序由3个代码文件共同生成,那么就会有3个.o格式目标文件,链接是把多个目标文件真正的联系在一起,比如a.o 中使用了 b.o中的一个函数,那么它们两个之间的地址是如何确定的(同理,可引申到使用函数库的问题,使用printf函数,也是需要链接器进行确定printf函数地址,才能知道如何调用。)这个就是链接器的作用)
宏是用于编译器处理的,他在程序编译时,会在对应位置展开成代码。。,这就相当于你在告诉编译器,我想在这个位置加一些代码,代码的内容已在宏中定义,请编译器自己支找。。。,也就是说程序在运行时,早已变成了对应位置上的代码,此时已没有宏的概念了。。。。
而函数则是运行时,调用。他不会在编译时,在对应位置上加上函数代码,只是加上一个函数入口指针。。。从这个入口去运行一段代码。。。运行完了之后回到当前位置继续执行。。。。
可以简单的认为,宏是在编译时上起作用,而函数是运行时起作用。。。
宏是一种预处理指令,它提供了一种机制,可以用来替换源代码中的字符串。
1、条件编译:
C语言中,预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行相应的转换,预处理过程还会删除程序中的注释和多余的空白符号。
预处理指令是以#开头的代码行,#必须是该行除了空白字符外的第一个字符。#后是指令关键字,在#和指令关键字之间允许存在若干空白字符。
使用宏进行条件编译的用法与使用宏防止多重引用类似。示例如下:
使用条件编译,方便程序员在调试程序的过程中,执行一些在程序发布后并不需要执行的指令。只要在需要调试的代码前加上_DEBUG的定义,就可以在调试程序的过程中输出调试信息。
这样方便查看程序在运行过程中有没有出现错误,定位错误出现的地方。而在程序发布之前,取消_DEBUG的定义就可以不再执行调试代码。
2、宏函数:
函数的调用是需要一定的时间和空间代价的。因为系统在调用函数时,需要保留"现场",即将程序要执行的指令的下一条指令的位置压入栈,然后转入调用函数去执行,调用完函数后再返回主调函数,恢复"现场",返回到栈里保存的的下一条指令的位置继续执行。
所以函数的调用需要额外的时间和空间代价。
而宏函数则不存在上述问题,宏函数在预编译时,同函数定义的代码来替换函数名,将函数代码段嵌入到当前程序,不会产生函数调用。
所以会省去普通函数保留现场恢复现场的时间,但因为要将定义的函数体嵌入到当前程序,所以不可避免的会占用额外的存储空间。
在频繁调用同一个宏的时候,该现象尤其明显。宏函数的示例定义如下:
#define MAX(a,b) ((a)(b)?(b):(a))
宏函数的优点在于避免函数调用,提高程序效率。
同时需要注意的是inline标识符。inline也将函数定义为内联的。但是使用内联函数需要注意的是:函数体必须十分简单,不能含有循环、条件、选择等复杂结构,否则就不能作为内联函数了。
事实上,有时候即便你没有将函数指定为内联函数,编译器也会将一些简单的函数作为内联函数处理,而对于一些复杂的函数,即使声明为内联函数,编译器也不会理会的。
inline函数的瓶颈就在于此,使用inline标识符将函数声明为内联的,但这只是一种提示,到底编译器有没有优化还依赖于编译器的实现,而使用宏函数则完全由代码本身控制。
但在使用宏函数的时候,需要明确的是宏函数只是简单的替换,需要注意括号的使用。
扩展资料:
宏的更多规则特性:
(1)宏名一般用大写。
(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义。
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号。
(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
(6)可以用#undef命令终止宏定义的作用域。
(7)宏定义不可以嵌套。
(8)字符串" "中永远不包含宏。
(9)宏定义不分配内存,变量定义分配内存。
(10)宏定义不存在类型问题,它的参数也是无类型的。
参考资料:
百度百科--宏定义
宏定义的基础知识。引用宏定义时,直接代入进行代换。
既然已经宏定义SUB(a) (a)-(a),而程序中出现的对应a的是a+b,那么就将a换为a+b代入表达式:
d=SUB(a+b)*c=(a+b)-(a+b)*c=(2+3)-(2+3)*5
直接用(a+b)-(a+b)代换SUB(a+b)。这一点和数学是不同的,不要强行往数学上靠。
的确是不可以当函数的参数,但是可以换一个间接的思路。
将宏定义放在另一个函数里,通过取地址的方式来把你原先想去处理的参数进行处理。
示例如下:
#includestdio.h
#define cal(m) {m = m + 10;}
int main(void)
{
int Val_0 = 20;
test(Val_0);
printf("%d", Val_0);
system("pause");
}
int test(int *n)
{
cal(*n);
printf("hello\n");
return 0;
}