【问题标题】:What's the difference in practice between inline and #define?inline 和#define 在实践中的区别是什么?
【发布时间】:2011-04-03 01:01:36
【问题描述】:

正如标题所说; inline 关键字和#define 预处理器指令在实践中的区别是什么?

【问题讨论】:

标签: c inline c-preprocessor


【解决方案1】:

#define 是一个预处理工具,具有宏语义。考虑一下,如果 max(a,b) 是一个宏定义为

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

例 1:

val = max(100, GetBloodSample(BS_LDL)) 会溢出额外的无辜血液,因为该函数实际上会被调用两次。这可能意味着实际应用程序的性能存在显着差异。

例 2:

val = max(3, schroedingerCat.GetNumPaws()) 这表明了程序逻辑的严重差异,因为这可能会意外返回小于 3 的数字 - 这是用户无法预料的。

例 3:

val = max(x, y++) 可能会增加一次以上y


使用内联函数,这些都不会发生。

主要原因是宏概念的目标是实现的透明度(文本代码替换),而内联目标是适当的语言概念,使调用语义对用户更加透明。

【讨论】:

  • 你应该把shroedingerCat.GetNumPaws()改成一个C函数,而不是C++。
  • 嗯,问题是关于 C 的;尽管 C++ 成员函数不时返回不同的东西更为常见,因为它们自然依赖于其对象的状态。
  • 感谢您提供出色示例的出色答案 =)
  • 我认为 Ex1 和 Ex2 之间没有区别,您在两个不同的值上调用 max。
  • 不同之处在于 #define 不计算 max() 而是做非常糟糕的事情。
【解决方案2】:

宏(使用#define 创建)总是按原样替换,并且可能存在双重评估问题。

另一方面,inline 纯粹是建议性的——编译器可以随意忽略它。在 C99 标准下,inline 函数也可以有外部链接,创建可以链接的函数定义。

【讨论】:

    【解决方案3】:

    函数(无论是否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 这样做有点人为,但我希望你能明白。)

    【讨论】:

      【解决方案4】:

      嗯,多行 #define 比内联函数更难编写和编辑。您可以像任何普通函数一样定义内联函数,并且可以毫无问题地定义变量。想象一下,您想在另一个函数中多次调用一个代码块,并且该代码块需要自己的变量:使用内联函数更容易(是的,您可以使用 #defines 和 do { ... } while (0);必须考虑)。

      此外,启用调试后,您通常会获得内联函数的“模拟”堆栈帧,这有时可能会使调试更容易(至少当您使用 gcc 的 -g 编译/链接并使用 GDB 进行调试时,您会得到这样的结果, IIRC)。你可以在你的内联函数中放置断点。

      除此之外,结果应该几乎相同,AFAIK。

      【讨论】:

        【解决方案5】:

        类似函数的宏在定义它们的地方为您提供绝对零的完整性检查。当你搞砸了一个宏时,它会在一个地方正常工作而在其他地方被破坏,直到你失去几个小时的工作/睡眠时间,你才会知道为什么。类函数宏不对数据进行操作,而是对源代码进行操作。有时这很好,例如,当您想要使用 FILELINE 内置函数的可重用调试语句时,但大多数情况下,内联也可以做到这一点函数,就像任何其他函数一样在定义点检查语法。

        【讨论】:

          猜你喜欢
          • 2012-02-16
          • 1970-01-01
          • 2019-07-24
          • 1970-01-01
          • 2012-01-31
          • 2023-04-08
          • 1970-01-01
          • 2011-09-20
          • 1970-01-01
          相关资源
          最近更新 更多