内联函数与预处理器宏

时间:2009-07-16 13:26:45

标签: c++ c macros inline

内联函数与预处理器宏有何不同?

14 个答案:

答案 0 :(得分:118)

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

内联函数是实体函数,其主体直接注入其调用站点。它们只能在函数调用合适的地方使用。

现在,就类似函数的上下文中使用宏与内联函数而言,请注意:

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

答案 1 :(得分:68)

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

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

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

如果您使用max(a++,b++),则会出现副作用(ab将增加两次)。 相反,使用(例如)

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

答案 2 :(得分:14)

Inline函数由编译器扩展,其中宏由预处理器扩展,这只是文本替换.Hence

  • 在函数调用期间进行类型检查时,宏调用期间没有类型检查。

  • 由于重新评估参数和操作顺序,在宏扩展期间可能会出现不期望的结果和低效率。例如

    #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关键字来返回值。

  • 内联函数可以重载

  • 传递给宏的标记可以使用名为Token-Pasting运算符的运算符##进行连接。

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

答案 3 :(得分:12)

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

Here是其他几个不太明显的要点。

答案 4 :(得分:10)

要为已经给出的内容添加另一个区别:您无法单步执行调试器中的#define,但可以逐步执行内联函数。

答案 5 :(得分:8)

宏忽略了名称空间。这使他们变得邪恶。

答案 6 :(得分:3)

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

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

  • 宏在预编译时展开,你不能用它们进行调试,但你可以使用内联函数。

-- good article: http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1

答案 7 :(得分:2)

内联函数将保持值语义,而预处理器宏只是复制语法。如果多次使用该参数,则可以使用预处理器宏获得非常微妙的错误 - 例如,如果参数包含具有执行两次的“i ++”等突变,则非常令人惊讶。内联函数不会出现此问题。

答案 8 :(得分:1)

内联函数在语法上的行为就像普通函数一样,为函数局部变量提供类型安全性和范围,如果是方法则提供对类成员的访问。 此外,在调用内联方法时,您必须遵守私有/受保护的限制。

答案 9 :(得分:1)

要了解宏和内联函数之间的区别,首先我们应该知道它们的确切含义以及何时使用它们。

功能

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

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

MACROS:

# 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

Inline关键字请求编译器将函数调用替换为函数的主体,此处的输出是正确的,因为它先对表达式求值然后再进行传递,从而减少了函数调用的开销,因为不需要存储返回地址,函数参数不需要堆栈存储器。

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

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

结论:

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

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

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

答案 10 :(得分:0)

在GCC中(我不确定其他人),声明一个内联函数,只是对编译器的一个暗示。在一天结束时,仍然需要编译器来决定它是否包含函数的主体。

内联函数和预处理器宏之间的差异相对较大。预处理器宏在一天结束时只是文本替换。您放弃了编译器执行检查参数和返回类型的类型检查的能力。对参数的评估是非常不同的(如果你传递给函数的表达式有副作用,你将有一个非常有趣的时间调试)。关于函数和宏的使用位置存在细微差别。例如,如果我有:

#define MACRO_FUNC(X) ...

MACRO_FUNC显然定义了函数体。需要特别小心,以便在所有情况下都可以正常运行,可以使用一个函数,例如写得不好的MACRO_FUNC会导致错误

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

可以使用正常功能,没有问题。

答案 11 :(得分:0)

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

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

通常,您应该将内联函数视为常规函数,并混合使用一些次要优化。与大多数优化一样,编译器需要决定它是否真正关心应用它。由于各种原因,编译器通常会高兴地忽略程序员内联函数的任何尝试。

答案 12 :(得分:0)

如果内部函数中存在任何迭代或递归语句,则内联函数将表现为函数调用,以防止重复执行指令。它有助于节省程序的整体内存。

答案 13 :(得分:-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()示例所示。

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