内联和#define之间的实践有什么区别?

时间:2010-08-24 08:03:26

标签: c inline c-preprocessor

正如标题所说; inline关键字和#define预处理程序指令之间的实践差异是什么?

6 个答案:

答案 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 内置函数的可重用调试语句时,但大多数情况下,它也可以使用内联来完成函数,在定义点检查语法,就像任何其他函数一样。