CC++中宏与函数有哪些本质区别,能详细阐述一下吗?

2026-04-12 10:382阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2506个文字,预计阅读时间需要11分钟。

C/C++中宏与函数有哪些本质区别,能详细阐述一下吗?

目录

一、宏和函数的比较

1.宏的优点

2.宏的缺点

3.宏的特性

4.总结宏和函数的区别

5.宏和函数的组合

二、宏和函数的命名约定

1.宏和函数的比较

2.宏的优点

3.宏通用的约定

目录
  • 一、宏和函数的对比
    • 1.宏的优点
    • 2.宏的缺点
    • 3.宏的独特性
    • 4.总结并整理宏和函数的区别
    • 5.有没有宏和函数的结合体
  • 二、宏和函数的命名约定

    一、宏和函数的对比

    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/C++宏与函数内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

    本文共计2506个文字,预计阅读时间需要11分钟。

    C/C++中宏与函数有哪些本质区别,能详细阐述一下吗?

    目录

    一、宏和函数的比较

    1.宏的优点

    2.宏的缺点

    3.宏的特性

    4.总结宏和函数的区别

    5.宏和函数的组合

    二、宏和函数的命名约定

    1.宏和函数的比较

    2.宏的优点

    3.宏通用的约定

    目录
    • 一、宏和函数的对比
      • 1.宏的优点
      • 2.宏的缺点
      • 3.宏的独特性
      • 4.总结并整理宏和函数的区别
      • 5.有没有宏和函数的结合体
    • 二、宏和函数的命名约定

      一、宏和函数的对比

      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/C++宏与函数内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!