内联函数与预处理器宏


Answers:


127

预处理器宏只是应用于代码的替换模式。它们几乎可以在您的代码中的任何地方使用,因为在开始任何编译之前,它们已被其扩展替换。

内联函数是实际函数,其主体直接注入其调用位置。它们只能在适当的函数调用处使用。

现在,至于在类似函数的上下文中使用宏还是内联函数,建议:

  • 宏不是类型安全的,并且无论它们在语法上是否正确,都可以对其进行扩展-编译阶段将报告由宏扩展问题导致的错误。
  • 宏可能会在您不期望的上下文中使用,从而导致问题
  • 宏更加灵活,因为它们可以扩展其他宏-而内联函数不一定要这样做。
  • 宏会因扩展而产生副作用,因为输入表达式会复制到出现在模式中的任何位置。
  • 内联函数并不能总是保证内联-有些编译器仅在发行版本中或经过专门配置时才这样做。同样,在某些情况下,可能无法进行内联。
  • 内联函数可以提供变量的作用域(尤其是静态变量),预处理器宏只能在代码块{...}中做到这一点,而静态变量的行为将不完全相同。

39
内联函数不能总是保证内联:因为如果这样做,编译器将不会内联,这会生成较慢的代码等。编译器进行了大量分析,而工程师不能这样做,并且做正确的事情。
马丁·约克

14
我相信递归函数是大多数编译器忽略内联的另一个示例。
LBushkin

在这种情况下,与C ++相比,C有什么重要区别吗?
rzetterberg 2011年

7
没有提到的一点是,内联会受到编译标志的影响。例如,当您以最大速度进行编译时(例如GCC -O2 / -O3),编译器将选择内联许多函数,但是当您以最小大小(-Os)进行编译时,通常将内联函数仅调用一次(或非常小的函数) )。对于宏,没有这样的选择。
dbrank0

可以使用内联函数时,宏不能用访问说明符(如私有或受保护的)覆盖。
Hit's

78

首先,预处理器宏只是在编译之前在代码中“复制粘贴”。因此没有类型检查,并且可能会出现一些副作用

例如,如果要比较两个值:

#define max(a,b) ((a<b)?b:a)

如果使用max(a++,b++)例如ab将出现副作用(或将增加两次)。而是使用(例如)

inline int max( int a, int b) { return ((a<b)?b:a); }

3
只是想添加到您的示例中,除了副作用之外,宏还可能带来额外的工作量,请考虑max(fibonacci(100), factorial(10000))将较大的工作量计算两次:(
watashiSHUN

每个人都在谈论类型检查,但是只是您提供了一个真实的示例,这就是为什么我赞成这个答案。
伊万津尼奥

16

内联函数由编译器扩展,而宏则由预处理器扩展,这仅仅是文本替换。

  • 在宏调用期间不进行类型检查,而在函数调用期间进行类型检查。

  • 由于重新评估参数和操作顺序,在宏扩展过程中可能会发生不良结果和低效率。例如

    #define MAX(a,b) ((a)>(b) ? (a) : (b))
    int i = 5, j = MAX(i++, 0);

    会导致

    int i = 5, j = ((i++)>(0) ? (i++) : (0));
  • 宏扩展之前不评估宏参数

    #define MUL(a, b) a*b
    int main()
    {
      // The macro is expended as 2 + 3 * 3 + 5, not as 5*8
      printf("%d", MUL(2+3, 3+5));
     return 0;
    }
    // Output: 16`
  • 像函数一样,不能在宏中使用return关键字返回值。

  • 内联函数可以重载

  • 传递给宏的令牌可以使用称为令牌粘贴操作符的操作符##进行串联。

  • 宏通常用于代码重用,因为使用内联函数可以消除函数调用期间的时间开销(多余的时间)(避免跳转到子例程)。


13

关键区别在于类型检查。编译器将检查您作为输入值传递的类型是否可以传递给函数。预处理器宏不是这样-它们在进行任何类型检查之前都会被扩展,并且可能导致严重且难以检测的错误。

这里列出了其他一些不太明显的要点。


11

要给已经给出的内容增加另一个区别:您不能#define在调试器中单步调试,但可以单步执行内联函数。



3

内联函数类似于宏(因为在编译时在调用时扩展了功能代码),内联函数由编译器解析,而宏由预处理器扩展。结果,有几个重要的区别:

  • 内联函数遵循在常规函数上强制执行的所有类型安全协议。
  • 内联函数使用与任何其他函数相同的语法来指定,除了它们在函数声明中包括inline关键字。
  • 作为参数传递给内联函数的表达式将被评估一次。
  • 在某些情况下,可以多次评估作为参数传递给宏的表达式。 http://msdn.microsoft.com/zh-CN/library/bf6bf4cf.aspx

  • 宏是在预编译时扩展的,您不能将其用于调试,但可以使用内联函数。

- 好文章:http://www.codeguru.com/forum/showpost.php?p= 1093923 &postcount=1

;


2

内联函数将保留值语义,而预处理器宏仅复制语法。如果多次使用该参数,则预处理器宏可能会引起非常微妙的错误-例如,如果该参数包含“ i ++”之类的突变,则两次执行该指令是很令人惊讶的。内联函数不会有此问题。


1

内联函数在语法上的行为类似于普通函数,它提供类型安全性,并提供函数局部变量的范围以及如果它是方法则可访问类成员。同样,在调用内联方法时,必须遵守私有/受保护的限制。


1

要了解宏函数和内联函数之间的区别,首先我们应该知道它们到底是什么以及何时使用它们。

功能

int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • 函数调用具有相关的开销,因为函数完成执行后,它必须知道它必须返回的位置,并且还需要将值存储在堆栈存储器中。

  • 对于小型应用程序来说,这不是问题,但是让我们以金融应用程序为例,其中每秒发生数以千计的交易,我们不能使用函数调用。

宏:

# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • 宏在预处理阶段起作用,即在此阶段用#关键字编写的语句将被内容替换,即

int结果=平方(x * x)

但是宏具有与之相关的错误。

#define Square(x) x*x
int main() {
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

这里的输出是11而不是36

内联函数

inline int Square(int x) {
    return x * x;
}

int main() {
    using namespace std;
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

输出36

内联关键字要求编译器将函数调用替换为函数体,此处的输出是正确的,因为它先对表达式求值然后传递,由于无需存储返回地址和堆栈,因此减少了函数调用的开销函数参数不需要内存。

宏和内联函数之间的比较:

  1. 宏通过替换工作,而在内联函数中,函数调用被替换为主体。
  2. 宏由于替换而容易出错,而内联函数可以安全使用。
  3. 宏没有地址,而内联函数有地址。
  4. 宏很难与多行代码一起使用,而内联函数却很难。
  5. 在C ++中,宏不能与成员函数一起使用,而内联函数可以。

结论:

内联函数有时比宏有用,因为它可以提高性能,使用安全并减少函数调用开销。这只是对编译器的请求,某些函数不会像这样内联:

  • 大型功能
  • 有太多条件参数的函数
  • 递归代码和带循环的代码等

这是一件好事,因为每当编译器认为最好以其他方式执行操作时,这都是好事。


谨此说明:宏可以固定为带有括号的相同数字。但是,它仍然容易出错,因为您需要在实现过程中考虑绝对的哑替代和所有情况。
麦克

0

在GCC中(我不确定其他人),内联声明一个函数只是对编译器的提示。最终还是由编译器决定何时调用它时是否包括函数的主体。

内联函数和预处理器宏之间的差异相对较大。预处理器宏只是一天结束时的文本替换。您放弃了很多让编译器对参数进行类型检查并返回类型的能力。对参数的求值有很大不同(如果传递给函数的表达式有副作用,则调试时会很有趣)。函数和宏的使用位置存在细微的差异。例如,如果我有:

#define MACRO_FUNC(X) ...

显然MACRO_FUNC定义了函数的主体。需要格外小心,以使其在所有情况下都能正常运行,例如可以使用一个功能,例如,编写不当的MACRO_FUNC会导致错误。

if(MACRO_FUNC(y)) {
 ...body
}

可以正常使用功能,而不会出现问题。


0

从编码的角度来看,内联函数就像一个函数。因此,内联函数和宏之间的差异与函数和宏之间的差异相同。

从编译的角度来看,内联函数类似于宏。它被直接注入到代码中,而不被调用。

通常,您应该将内联函数视为常规函数,并混入一些较小的优化。与大多数优化一样,由编译器决定是否确实愿意应用它。通常,由于各种原因,编译器会很乐意忽略程序员对内联函数的任何尝试。


0

如果其中存在任何迭代或递归语句,则内联函数将充当函数调用,以防止重复执行指令。这对于节省程序的整体内存很有帮助。


-1
#include<iostream>
using namespace std;
#define NUMBER 10 //macros are preprocessed while functions are not.
int number()
{ 
    return 10;
}
/*In macros, no type checking(incompatible operand, etc.) is done and thus use of micros can lead to errors/side-effects in some cases. 
However, this is not the case with functions.
Also, macros do not check for compilation error (if any). Consider:- */
#define CUBE(b) b*b*b
int cube(int a)
{
 return a*a*a;
}
int main()
{
 cout<<NUMBER<<endl<<number()<<endl;
 cout<<CUBE(1+3); //Unexpected output 10
 cout<<endl<<cube(1+3);// As expected 64
 return 0;
}

宏通常比函数快,因为它们不涉及实际的函数调用开销。

宏的一些缺点:没有类型检查。难以调试,因为它们会导致简单替换。宏没有名称空间,因此代码的一个部分中的宏会影响另一部分。宏可能会导致副作用,如上面的CUBE()示例所示。

宏通常是一个班轮。但是,它们可以由多行组成。函数中没有这样的约束。


如何更有趣,你怎么得#define TWO_N(n) 2 << n,然后cout << CUBE(TWO_N(3 + 1)) << endl;?(结束输出行endl比开始输出行更好。)
Jonathan Leffler
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.