内联函数与预处理器宏有何不同?
14 回答
预处理器宏只是应用于您的代码的替换模式。它们几乎可以在您的代码中的任何地方使用,因为它们在任何编译开始之前被它们的扩展替换。
内联函数是其主体直接注入其调用站点的实际函数。它们只能在函数调用合适的地方使用。
现在,就在类似函数的上下文中使用宏与内联函数而言,请注意:
- 宏不是类型安全的,无论它们在语法上是否正确都可以扩展 - 编译阶段将报告由宏扩展问题导致的错误。
- 宏可以在您不期望的上下文中使用,从而导致问题
- 宏更灵活,因为它们可以扩展其他宏 - 而内联函数不一定这样做。
- 宏可能会因为它们的扩展而产生副作用,因为输入表达式会被复制到它们出现在模式中的任何位置。
- 内联函数并不总是保证被内联 - 一些编译器只在发布版本中这样做,或者当它们被专门配置为这样做时。此外,在某些情况下可能无法进行内联。
- 内联函数可以为变量(尤其是静态变量)提供范围,预处理器宏只能在代码块 {...} 中执行此操作,而静态变量的行为方式不会完全相同。
首先,预处理器宏只是在编译之前在代码中“复制粘贴”。所以没有类型检查,会出现一些副作用
例如,如果要比较 2 个值:
#define max(a,b) ((a<b)?b:a)
max(a++,b++)
如果您使用例如(a
或b
将增加两次) ,则会出现副作用。相反,使用(例如)
inline int max( int a, int b) { return ((a<b)?b:a); }
内联函数由编译器扩展,而宏由预处理器扩展,这仅仅是文本替换。
因此,
在宏调用期间没有类型检查,而在函数调用期间进行类型检查。
由于重新评估参数和操作顺序,宏扩展期间可能会出现不希望的结果和低效率。例如:
#define MAX(a,b) ((a)>(b) ? (a) : (b)) int i = 5, j = MAX(i++, 0);
会导致
int i = 5, j = ((i++)>(0) ? (i++) : (0));
宏参数在宏扩展之前不被评估
#include <stdio.h> #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 关键字不能像函数一样在宏中使用来返回值。
内联函数可以重载。
传递给宏的标记可以使用
##
称为标记粘贴操作符的操作符连接。宏通常用于代码重用,因为内联函数用于消除函数调用期间的时间开销(多余时间)(避免跳转到子程序)。
关键区别在于类型检查。编译器将检查您作为输入值传递的内容是否属于可以传递给函数的类型。预处理器宏并非如此——它们在任何类型检查之前被扩展,这可能会导致严重且难以检测的错误。
以下是其他几个不太明显的要点。
为已经给出的内容添加另一个区别:您不能#define
在调试器中单步执行 a ,但可以单步执行内联函数。
宏忽略命名空间。这使他们变得邪恶。
内联函数类似于宏(因为函数代码在编译时在调用点展开),内联函数由编译器解析,而宏由预处理器展开。因此,有几个重要的区别:
- 内联函数遵循对正常函数强制执行的所有类型安全协议。
- 内联函数使用与任何其他函数相同的语法来指定,只是它们在函数声明中包含 inline 关键字。
- 作为参数传递给内联函数的表达式只计算一次。
在某些情况下,作为参数传递给宏的表达式可以被多次计算。 http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx
宏在预编译时扩展,您不能将它们用于调试,但您可以使用内联函数。
--好文章:http://www.codeguru.com/forum/showpost.php?p=1093923&postcount= 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;
}
- 在预处理阶段应用宏。在此阶段,用
#define
关键字编写的语句将被替换或扩展
整数结果 = 平方(x*x)
但是宏可能会导致意外行为。
#define Square(x) x*x
int main()
{
int val = 5;
int result = Square(val + 1);
cout << result << endl;
}
这里的输出是11,而不是 36。
内联函数:
inline int Square(int x)
{
return x * x;
}
int main()
{
int val = 5;
int result = Square(val + 1);
cout << result << endl;
}
输出:36
该inline
关键字要求编译器将函数调用替换为函数体。这里的输出是正确的,因为它首先计算表达式,然后使用结果来执行函数的主体。内联函数减少了函数调用开销,因为不需要将返回地址或函数参数存储到堆栈中。
宏和内联函数的比较:
- 宏通过文本替换工作,而内联函数复制函数的逻辑。
- 由于替换,宏很容易出错,而内联函数可以安全使用。
- 宏不能分配给函数指针;内联函数可以。
- 宏很难与多行代码一起使用,而内联函数则不然。
- 在 C++ 中,宏不能与成员函数一起使用,而内联函数可以。
结论:
内联函数有时比宏更有用,因为它们使用安全,但也可以减少函数调用开销。inline
关键字是对编译器的请求,某些函数不会被内联,例如:
- 大型函数
- 具有太多条件参数的函数
- 递归代码和带有循环的代码等。
这是一件好事,因为它允许编译器确定以另一种方式做事是否更好。
内联函数将维护值语义,而预处理器宏只复制语法。如果您多次使用该参数,您可能会在预处理器宏中遇到非常微妙的错误——例如,如果参数包含像“i++”这样的突变,那么执行两次是相当令人惊讶的。内联函数不会有这个问题。
内联函数在语法上的行为就像普通函数一样,提供类型安全性和函数局部变量的范围以及对类成员的访问(如果它是一个方法)。此外,在调用内联方法时,您必须遵守私有/受保护的限制。
在 GCC(我不确定其他人)中,声明内联函数只是对编译器的提示。最终还是由编译器决定是否在调用时包含函数体。
内联函数和预处理宏的区别比较大。预处理器宏只是一天结束时的文本替换。您放弃了编译器对参数和返回类型进行类型检查的许多能力。参数的评估有很大的不同(如果你传递给函数的表达式有副作用,你将有一个非常有趣的调试时间)。关于函数和宏的使用位置存在细微差别。例如,如果我有:
#define MACRO_FUNC(X) ...
MACRO_FUNC 显然定义了函数的主体。需要特别小心,以便在所有情况下都能正确运行一个函数,例如一个写得不好的 MACRO_FUNC 会导致错误
if(MACRO_FUNC(y)) {
...body
}
在那里可以毫无问题地使用正常功能。
从编码的角度来看,内联函数就像一个函数。因此,内联函数和宏之间的区别与函数和宏之间的区别相同。
从编译的角度来看,内联函数类似于宏。它直接注入到代码中,而不是调用。
通常,您应该将内联函数视为混合了一些小的优化的常规函数。和大多数优化一样,由编译器决定它是否真的关心应用它。由于各种原因,编译器通常会很高兴地忽略程序员内联函数的任何尝试。
内联函数如果存在任何迭代或递归语句,将表现为函数调用,以防止重复执行指令。它对节省程序的整体内存很有帮助。
#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() 示例所示,宏可能会导致副作用。
宏通常是一个衬里。但是,它们可以包含多于一行。函数中没有这样的限制。