正如标题所说; inline关键字和#define预处理程序指令之间的实践差异是什么?
答案 0 :(得分:40)
#define
是一个预处理器工具,具有宏语义。如果max(a,b)
是定义为
#define max(a,b) ((a)>(b)?(a):(b))
:
Ex 1:
val = max(100, GetBloodSample(BS_LDL))
会溢出额外的无辜血液,因为该功能实际上会被调用两次。这可能意味着实际应用程序的显着性能差异。
前2:
val = max(3, schroedingerCat.GetNumPaws())
这表明程序逻辑存在严重差异,因为这可能会意外地返回一个小于3的数字 - 这是用户不会期望的。
前3:
val = max(x, y++)
可能会多次增加y
次。
使用内联函数,这些都不会发生。
主要原因是宏观概念的目标是实现的透明度(文本代码替换)和内联目标正确的语言概念,使得调用语义对用户更加透明。
答案 1 :(得分:2)
函数(无论是否inline
)和宏实现不同的目的。他们之间的区别不应该被看作是一种意识形态,因为有些人似乎会接受它,而更重要的是,它们可以很好地协同工作。
宏是在编译时完成的文本替换,它们可以执行
之类的操作#define P99_ISSIGNED(T) ((T)-1 < (T)0)
它为您提供编译时表达式,表示是否对整数类型进行了签名。也就是说,当表达式的类型未知时(在定义中)并且您想要对其执行某些操作时,理想情况下使用它们。另一方面,宏的缺陷是它们的参数可能会被评估几次,这是因为副作用而不好。
另一种类型的函数(inline
)是打字的,这使得它们更严格,或者说是消极的,不那么灵活。考虑函数
inline uintmax_t absU(uintmax_t a) { return a; }
inline uintmax_t absS(uintmax_t a) {
return (-a < a) ? -a : a;
}
第一个为无符号整数类型实现了普通的abs
函数。第二个为签名类型实现它。 (是的,它需要一个无符号的参数,这是出于目的。)
我们可以使用任何整数类型。但是,返回类型总是具有最大宽度,并且知道如何在两者之间进行选择存在一定的困难。
现在使用以下宏
#define ABS(T, A) ((T)(P99_ISSIGNED(T) ? absS : absU)(A))
我们实施了
(好吧,我承认用abs
做这件事有点人为,但我希望你能得到这个。)
答案 2 :(得分:1)
宏(使用#define
创建)始终以书面形式替换,并且可能存在双重评估问题。
inline
纯粹是建议性的 - 编译器可以自由地忽略它。根据C99标准,inline
函数也可以具有外部链接,创建可以链接的函数定义。
答案 3 :(得分:1)
嗯,多行#define
比内联函数更难编写和编辑。您可以像任何普通函数一样定义内联函数,并且可以毫无问题地定义变量。想象一下,你想在另一个函数中多次调用一个代码块,并且该代码块需要它自己的变量:使用内联函数更容易(是的,你可以使用#defines和do { ... } while (0);
来实现它,但它是你需要考虑的事情。)
此外,通过启用调试,您通常可以获得内联函数的“模拟”堆栈框架,这有时可以使调试更容易(至少这是您使用gcc -g
编译/链接时所获得的内容,并使用GDB,IIRC)。您可以在内联函数中放置断点。
除此之外,结果应该几乎相同,AFAIK。
答案 4 :(得分:0)
这里的描述非常深入: http://www.geekinterview.com/question_details/23831 欢呼声
答案 5 :(得分:0)
类似函数的宏在它们被定义的位置给你绝对零理智检查。当你搞砸了一个宏时,它会在一个地方工作正常并在其他地方被打破,你不会知道为什么直到你失去了几个小时的工作/睡眠时间。类似函数的宏不对数据进行操作,它们对源代码进行操作。有时这很好,例如当您需要使用 FILE 和 LINE 内置函数的可重用调试语句时,但大多数情况下,它也可以使用内联来完成函数,在定义点检查语法,就像任何其他函数一样。