#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;
}
13 回答
首先,预处理器宏在编译之前的代码中只是"copy paste" . 所以没有 type checking ,有些 side effects 可以出现
例如,如果要比较2个值:
如果您使用
max(a++,b++)
(例如a
或b
将递增两次),则会出现副作用 . 相反,使用(例如)预处理器宏只是应用于代码的替换模式 . 它们几乎可以在代码中的任何位置使用,因为在任何编译开始之前它们都会被扩展替换 .
内联函数是实体函数,其主体直接注入其调用站点 . 它们只能在适合函数调用的地方使用 .
现在,就类似函数的上下文中使用宏与内联函数而言,请注意:
宏不是类型安全的,并且可以扩展,无论它们是否在语法上是正确的 - 编译阶段将报告由宏扩展问题导致的错误 .
宏可以在您不期望的上下文中使用,从而导致出现问题
宏更灵活,因为它们可以扩展其他宏 - 而内联函数不一定会这样做 .
宏因为扩展而导致副作用,因为输入表达式会复制到模式中出现的任何位置 .
内联函数并不总是保证内联 - 一些编译器只在发布版本中执行此操作,或者在专门配置它们时执行此操作 . 此外,在某些情况下,内联可能是不可能的 .
内联函数可以为变量(特别是静态函数)提供范围,预处理器宏只能在代码块中执行此操作,而静态变量的行为方式不会完全相同 .
内联函数由编译器扩展,其中宏由预处理器扩展,这仅仅是文本替换.Hence
在函数调用期间完成类型检查时,在宏调用期间没有类型检查 .
由于重新评估参数和操作顺序,在宏扩展期间可能会出现不期望的结果和低效率 . 例如
会导致
无法在宏中使用return关键字来返回值,就像函数一样 .
内联函数可以重载
传递给宏的标记可以使用名为Token-Pasting运算符的运算符##连接 .
宏通常用于代码重用,其中内联函数用于消除函数调用期间的时间开销(超时)(避免跳转到子例程) .
关键的区别是类型检查 . 编译器将检查您作为输入值传递的是否是可以传递给函数的类型 . 对于预处理器宏来说情况并非如此 - 它们在任何类型检查之前都会被扩展,这会导致严重且难以检测到的错误 .
Here是其他几个不太明显的要点 .
要为已经给出的内容添加另一个区别:您无法在调试器中单步执行
#define
,但可以单步执行内联函数 .宏忽略了名称空间 . 这使他们变得邪恶 .
内联函数类似于宏(因为函数代码在编译时在调用时展开),内联函数由编译器解析,而宏则由预处理器扩展 . 因此,有几个重要的区别:
内联函数遵循在正常函数上强制执行的所有类型安全协议 .
内联函数使用与任何其他函数相同的语法指定,除了它们在函数声明中包含inline关键字 .
作为内联函数的参数传递的表达式被计算一次 .
在某些情况下,作为参数传递给宏的表达式可以多次计算 . http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx
宏在预编译时展开,不能用它们进行调试,但可以使用内联函数 .
-- good article: http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1
;
内联函数将保持值语义,而预处理器宏只是复制语法 . 如果使用参数,则可以使用预处理器宏获得非常微妙的错误多次 - 例如,如果参数包含像“i”那样执行两次的突变,那就非常令人惊讶了 . 内联函数不会出现此问题 .
内联函数在语法上的行为就像普通函数一样,为函数局部变量提供类型安全性和范围,如果是方法则提供对类成员的访问 . 此外,在调用内联方法时,您必须遵守私有/受保护的限制 .
在GCC中(我不确定其他人),声明一个内联函数,只是对编译器的一个暗示 . 在一天结束时,仍然需要编译器决定它是否包含函数体,无论何时调用它 .
内联函数和预处理器宏之间的差异相对较大 . 预处理器宏在一天结束时只是文本替换 . 您放弃了编译器执行检查参数和返回类型的类型检查的能力 . 对参数的评估是非常不同的(如果你传递给函数的表达式有副作用,你将有一个非常有趣的时间调试) . 关于函数和宏的使用位置存在细微差别 . 例如,如果我有:
其中MACRO_FUNC显然定义了函数体 . 需要特别小心,以便在所有情况下都可以正常运行,可以使用一个函数,例如写得不好的MACRO_FUNC会导致错误 .
可以使用正常功能而没有问题 .
从编码的角度来看,内联函数就像一个函数 . 因此,内联函数和宏之间的差异与函数和宏之间的差异相同 .
从编译的角度来看,内联函数类似于宏 . 它被直接注入到代码中,而不是被调用 .
通常,您应该将内联函数视为常规函数,并将一些次要优化混合在一起 . 与大多数优化一样,由编译器决定它是否真正关心应用它 . 由于各种原因,编译器通常会高兴地忽略程序员内联函数的任何尝试 .
如果在其中存在任何迭代或递归语句,则内联函数将表现为函数调用,以防止重复执行指令 . 它有助于节省程序的整体内存 .
宏通常比函数更快,因为它们不涉及实际的函数调用开销 .
宏的一些缺点:没有类型检查 . 难以调试,因为它们导致简单的替换 . 宏没有命名空间,因此一段代码中的宏可以影响其他部分 . 宏可能会导致副作用,如上面的CUBE()示例所示 .
宏通常是一个班轮 . 但是,它们可以包含多行 . 功能中没有这样的约束 .