C/C++细数宏与函数有那些区别

 更新时间:2022年10月26日 15:26:55   作者:不一样的烟火a  
在C程序中,可以用宏代码提高执行效率。宏代码本身不是函数,但使用起来象函数。预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return等过程,从而提高了速度

一、宏和函数的对比

1.宏的优点

  • 宏通常被应用于执行简单的运算。
  • 比如在两个数中找出较大的一个。
#define MAX(a, b) ((a)>(b)?(a):(b))

那为什么不用函数来完成这个任务?

原因有两点:

用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。

所以宏比函数在程序的规模和速度方面更胜一筹。

举例:

用宏实现两个数中找出较大值。

#define MAX(x, y) ((x) > (y) ? (x) : (y))
int main()
{
	int a = 10;
	int b = 20;
	int c = MAX(a, b); // 宏
	return 0;
}

转到反汇编,查看汇编代码。

    int c = MAX(a, b); // 宏
00791783  mov         eax,dword ptr [a]  
00791786  cmp         eax,dword ptr [b]  
00791789  jle         __$EncStackInitStart+3Ah (0791796h)  
0079178B  mov         ecx,dword ptr [a]  
0079178E  mov         dword ptr [ebp-0E8h],ecx  
00791794  jmp         __$EncStackInitStart+43h (079179Fh)  
00791796  mov         edx,dword ptr [b]  
00791799  mov         dword ptr [ebp-0E8h],edx  
0079179F  mov         eax,dword ptr [ebp-0E8h]  
007917A5  mov         dword ptr [c],eax

用函数实现两个数中找出较大值。

int Max(int x, int y)
{
	return ((x) > (y) ? (x) : (y));
}
int main()
{
	int a = 10;
	int b = 20;
	int c = Max(a, b); // 函数
	return 0;
}

调用函数的汇编代码。

    int c = Max(a, b); // 函数
002C1793  mov         eax,dword ptr [b]  
002C1796  push        eax  
002C1797  mov         ecx,dword ptr [a]  
002C179A  push        ecx  
002C179B  call        _Max (02C139Dh)  // 调用Max函数
002C17A0  add         esp,8  
002C17A3  mov         dword ptr [c],eax  

计算的汇编代码。

int Max(int x, int y)
{
002C1DF0  push        ebp  
002C1DF1  mov         ebp,esp  
002C1DF3  sub         esp,0C4h  
002C1DF9  push        ebx  
002C1DFA  push        esi  
002C1DFB  push        edi  
002C1DFC  lea         edi,[ebp-4]  
002C1DFF  mov         ecx,1  
002C1E04  mov         eax,0CCCCCCCCh  
002C1E09  rep stos    dword ptr es:[edi]  
002C1E0B  mov         ecx,offset _66EADA86_详解预处理\test@c (02CC000h)  
002C1E10  call        @__CheckForDebuggerJustMyCode@4 (02C1307h)  
// 下面这些才是计算的代码,上面这些代码可以说还是在为调用函数做准备
// 并且可以看出下面的汇编代码和宏那里的汇编代码是一样的
    return ((x) > (y) ? (x) : (y));
002C1E15  mov         eax,dword ptr [x]  
002C1E18  cmp         eax,dword ptr [y]  
002C1E1B  jle         __$EncStackInitStart+2Ch (02C1E28h)  
002C1E1D  mov         ecx,dword ptr [x]  
002C1E20  mov         dword ptr [ebp-0C4h],ecx  
002C1E26  jmp         __$EncStackInitStart+35h (02C1E31h)  
002C1E28  mov         edx,dword ptr [y]  
002C1E2B  mov         dword ptr [ebp-0C4h],edx  
002C1E31  mov         eax,dword ptr [ebp-0C4h]  
}

函数返回的汇编代码。

002C1E37  pop         edi  
002C1E38  pop         esi  
002C1E39  pop         ebx  
002C1E3A  add         esp,0C4h  
002C1E40  cmp         ebp,esp  
002C1E42  call        __RTC_CheckEsp (02C1230h)  
002C1E47  mov         esp,ebp  
002C1E49  pop         ebp  
002C1E4A  ret

总结:

如果用函数的话,会经过以下几个步骤:

  • 函数调用。
  • 计算。
  • 函数返回。

根据上面的汇编代码可以看出,函数调用和函数返回所用的汇编指令远多于计算所用的汇编指令,这就导致函数调用和返回所用的时间远多于计算所用的时间。而宏本质是替换,不用进行函数调用和返回,所以这就是宏在实现小型计算工作时比函数快的原因。

  • 更为重要的是函数的参数必须声明为特定的类型。

所以函数只能在类型合适的表达式上使用。

而宏是类型无关的,宏可以适用于整形、长整型、浮点型等,可以用于 “>” 来比较的类型。

举例:

下面为宏和函数实现的两个数中找出较大值。

// 宏
#define MAX(x, y) ((x) > (y) ? (x) : (y))
// 函数
int Max(int x, int y)
{
	return ((x) > (y) ? (x) : (y));
}

可以看出,该函数只能对两个int类型的数进行比较,而宏却可以对两个任意类型的数进行比较,这就使宏用起来更加的灵活。

2.宏的缺点

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  2. 宏是没法调试的。
  3. 宏由于类型无关,也就不够严谨。
  4. 宏可能会带来运算符优先级的问题,导致程容易出现错。

3.宏的独特性

宏有时候可以做函数做不到的事情。

比如:宏的参数可以出现类型,但是函数做不到。

#define MALLOC(num, type) (type*)malloc(num * sizeof(type))
int main()
{
	// 使用
	int* p1 = MALLOC(10, int); // 替换后为 int* p1 = (int*)malloc(10 * sizeof(int));
	char* p2 = MALLOC(5, int); // 替换后为 int* p2 = (char*)malloc(5 * sizeof(char));
	return 0;
}

4.总结并整理宏和函数的区别

属 性#define定义宏函数
代 码 长 度每次使用时,宏代码都会被 插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执 行 速 度更快存在函数的调用和返回的额外开销,所以相对慢一些
操 作 符 优 先 级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预 测。
带 有 副 作 用 的 参 数参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一 次,结果更容易控制。
参 数 类 型宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型。函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是 不同的。
调 试宏是不方便调试的函数是可以逐语句调试的
递 归宏是不能递归的函数是可以递归的

5.有没有宏和函数的结合体

答案是当然有。

在C99和C++里面都有一个东西叫做内联函数(inline)

内联函数既有函数的优点又有宏的优点:

  • 宏的优点:内联函数没有函数的调用和返回。
  • 函数的优点: 内联函数本身是个函数,它没有参数优先级、副作用等宏的缺点。

提示:由于本篇文章主要是讲宏和函数的区别,内联函数就不多讲,这里只做了解,后面我会单独写一篇文章来讲解内联函数的。

二、宏和函数的命名约定

一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。

那么我们平时就应该有一个良好的书写习惯:

把宏名全部大写

函数名不要全部大写

这里可以参考《高质量C/C++编程指南》这本书,有兴趣的小伙伴可以去看看。

到此这篇关于C/C++细数宏与函数有那些区别的文章就介绍到这了,更多相关C/C++宏与函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解C++的模板中typename关键字的用法

    详解C++的模板中typename关键字的用法

    在C++的Template中我们经常可以见到使用typename来定义类型名称,更加具体的我们就在接下来为大家详解C++的模板中typename关键字的用法,需要的朋友可以参考下:
    2016-06-06
  • c++面试题字符串拷贝函数示例

    c++面试题字符串拷贝函数示例

    这个也算是企业招工里面比较常见的一道笔试面试题了,非常简单。个人觉得考的主要是对指针使用的熟练程度,还有对字符串类内部原理的掌握程度
    2013-12-12
  • C语言之结构体(struct)详解

    C语言之结构体(struct)详解

    本文主要介绍C语言 结构体的知识,学习C语言肯定需要学习结构体,这里详细说明了结构体并附示例代码,供大家参考学习,有需要的小伙伴可以参考下
    2021-10-10
  • Qt菜单QMenu和菜单栏QMenuBar及自定义菜单用法

    Qt菜单QMenu和菜单栏QMenuBar及自定义菜单用法

    本文主要介绍了Qt菜单QMenu和菜单栏QMenuBar及自定义菜单用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • C++音乐播放按钮的封装过程详解

    C++音乐播放按钮的封装过程详解

    此篇文章用于记录学习C++封装音乐播放按钮,封装将对象的属性和行为作为一个整体,表现生活中的事物、将属性和行为加以权限控制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • Qt实现带字数限制的文字输入框

    Qt实现带字数限制的文字输入框

    这篇文章介绍了Qt实现带字数限制文字输入框的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • C++ 单例模式的几种实现方式研究

    C++ 单例模式的几种实现方式研究

    单例模式,可以说设计模式中最常应用的一种模式了,据说也是面试官最喜欢的题目。但是如果没有学过设计模式的人,可能不会想到要去应用单例模式,面对单例模式适用的情况
    2019-01-01
  • QT5连接MySQL实现增删改查

    QT5连接MySQL实现增删改查

    这篇文章主要为大家详细介绍了QT5如何连接MySQL实现增删改查功能,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的可以了解一下
    2022-12-12
  • C语言基础知识点解析(extern,static,typedef,const)

    C语言基础知识点解析(extern,static,typedef,const)

    本篇文章是对C语言基础知识点(extern,static,typedef,const)的用法进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-10-10
  • C++详细讲解函数调用与Struct和CLass的区别

    C++详细讲解函数调用与Struct和CLass的区别

    主调函数使用被调函数的功能,称为函数调用。在C语言/C++中,只有在函数调用时,函数体中定义的功能才会被执行,下面让我们详细来了解
    2022-05-05

最新评论