【问题标题】:What does extern inline do?外联内联做什么?
【发布时间】:2010-09-18 00:10:09
【问题描述】:

我知道inline 本身是对编译器的建议,它可能会或可能不会内联函数,它也会产生可链接的目标代码。

我认为static inline 的作用相同(可能内联也可能不内联),但内联时不会生成可链接的目标代码(因为没有其他模块可以链接到它)。

extern inline 在图片中的哪个位置?

假设我想用一个内联函数替换一个预处理器宏,并要求这个函数被内联(例如,因为它使用 __FILE____LINE__ 宏,它们应该为调用者解析,但不是这个被调用函数)。也就是说,如果函数没有内联,我想查看编译器或链接器错误。 extern inline 会这样做吗? (我认为,如果没有,除了坚持使用宏之外,没有其他方法可以实现此行为。)

C++ 和 C 有区别吗?

不同的编译器供应商和版本之间有区别吗?

【问题讨论】:

    标签: c++ c extern inline-functions


    【解决方案1】:

    在 K&R C 或 C89 中,内联不是语言的一部分。许多编译器将其作为扩展实现,但没有关于它如何工作的定义语义。 GCC 是最早实现内联的,并引入了inlinestatic inlineextern inline 构造;大多数 C99 之前的编译器通常会效仿。

    GNU89:

    • inline:该函数可能是内联的(虽然这只是一个提示)。离线版本始终会发出并在外部可见。因此,您只能在一个编译单元中定义这样的内联,而其他每个编译单元都需要将其视为一个外联函数(否则您将在链接时得到重复的符号)。
    • extern inline 不会生成外联版本,但可能会调用一个(因此您必须在其他编译单元中定义它。但是,单定义规则适用;外联版本必须具有相同的代码作为此处提供的内联代码,以防编译器调用它。
    • static inline 不会生成外部可见的离线版本,尽管它可能会生成文件静态版本。单一定义规则不适用,因为从来没有发出外部符号,也没有调用一个。

    C99(或 GNU99):

    • inline: 像 GNU89 "extern inline";没有发出外部可见的函数,但可能会调用一个函数,因此必须存在
    • extern inline: 像 GNU89 "inline": 发出外部可见的代码,所以最多一个翻译单元可以使用它。
    • static inline:就像 GNU89 “静态内联”。这是 gnu89 和 c99 之间唯一的便携式

    C++:

    在任何地方都内联的函数必须在任何地方都是内联的,并且具有相同的定义。编译器/链接器将整理符号的多个实例。 static inlineextern inline 没有定义,尽管许多编译器都有它们(通常遵循 gnu89 模型)。

    【讨论】:

    • 在经典经典 C 中,'inline' 不是关键字;它可用作变量名。这将适用于 C89 和准标准 (K&R) C。
    • 看来你是对的。固定的。我以为它在 C89 中被保留为关键字(虽然不是在 K&R 中),但我似乎记错了
    • 我想补充一点,对于 Microsoft 的 Visual C++,有一个 __forceinline 关键字可以强制你的函数被内联。这显然是仅针对 VC++ 的编译器特定扩展。
    • C99“extern inline”和没有说明符有什么区别吗?
    • @Noora gcc 等效项是__attribute__((always_inline))。即使禁用了优化,它也会强制内联函数。它对极其频繁地调用的极短函数很有用,例如在向量数学库中,如果它们没有内联,则在优化关闭时会使调试运行缓慢。
    【解决方案2】:

    我相信您基于此声明误解了 __FILE__ 和 __LINE__:

    因为它使用 __FILE__ 和 __LINE__ 宏应该为调用者解析,但不是这个被调用者 功能

    编译有几个阶段,预处理是第一个。 __FILE__ 和 __LINE__ 在该阶段被替换。因此,当编译器可以考虑内联函数时,它们已经被替换了。

    【讨论】:

      【解决方案3】:

      听起来你想写这样的东西:

      inline void printLocation()
      {
        cout <<"You're at " __FILE__ ", line number" __LINE__;
      }
      
      {
      ...
        printLocation();
      ...
        printLocation();
      ...
        printLocation();
      

      并希望您每次都能打印出不同的值。正如 Don 所说,您不会,因为 __FILE__ 和 __LINE__ 是由预处理器实现的,而 inline 是由编译器实现的。因此,无论您从哪里调用 printLocation,都会得到相同的结果。

      唯一的方法是让 printLocation 成为一个宏。 (是的,我知道...)

      #define PRINT_LOCATION  {cout <<"You're at " __FILE__ ", line number" __LINE__}
      
      ...
        PRINT_LOCATION;
      ...
        PRINT_LOCATION;
      ...
      

      【讨论】:

      • 一个常见的技巧是宏 PRINT_LOCATION 调用函数 printLocation,传递 FILELINE 作为参数。当函数体不平凡时,这可以导致更好的调试器/编辑器/等行为。
      • @Roddy 看看我的解决方案 - 扩展您的解决方案,但更全面和可扩展。
      • @SteveJessop 类似我在下面的解决方案中列出的内容?
      【解决方案4】:

      我没有回答“它有什么作用?”,而是回答“我如何让它做我想做的事?”有 5 种内联,在 GNU C89、标准 C99 和 C++ 中都可用。 MSVC 有 一些 (注意我没有测试 MSVC 代码)

      总是内联,除非地址被占用

      __attribute__((always_inline)) 添加到任何声明中,然后使用其中一个 以下情况以处理其地址被占用的可能性。

      你可能永远不应该使用它,除非你需要它的语义(例如,以某种方式影响程序集,或者使用alloca)。编译器通常比你更清楚它是否值得。

      MSVC 的 __forceinline 看起来基本相同,但显然它拒绝在其他编译器管理得很好的很多常见情况下(例如,当优化关闭时)内联。

      内联并发出一个弱符号(如 C++,又名“让它工作")

      __attribute__((weak))
      void foo(void);
      inline void foo(void) { ... }
      

      请注意,这会留下一堆相同代码的副本,并且链接器会任意选择一个。

      MSVC 在 C 模式下似乎没有完全相同的功能,尽管有一些类似的东西。 __declspec(selectany) 似乎只在谈论数据,所以可能不适用于函数?还有对weak aliases 的链接器支持,但这在这里有效吗?

      内联,但从不发出任何符号(留下外部引用)

      __attribute__((gnu_inline))
      extern inline void foo(void) { ... }
      

      MSVC 的 __declspec(dllimport) 结合实际定义(否则不寻常),据说是这样做的。

      emit always(对于一个 TU,解决前面的问题)

      提示版本在 C++ 中发出一个弱符号,但在 C 的任一方言中发出一个强符号:

      void foo(void);
      inline void foo(void) { ... }
      

      或者您也可以不使用提示,这两种语言都会发出强烈的符号:

      void foo(void) { ... }
      

      通常,您在提供定义时就知道您的 TU 是什么语言,并且可能不需要太多内联。

      MSVC 的 __declspec(dllexport) 应该是这样做的。

      在每个 TU 中内联和发射

      static inline void foo(void) { ... }
      

      对于除static 之外的所有这些,您可以在上面添加void foo(void) 声明。这有助于编写干净的标头的“最佳实践”,然后#include使用内联定义创建一个单独的文件。然后,如果使用 C 风格的内联,#define 在一个专用 TU 中使用不同的宏来提供外联定义。

      如果标头可能同时用于 C 和 C++,请不要忘记 extern "C"


      还有一些相关的事情:

      从不内联

      __attribute__((noinline)) 添加到函数的任何声明中。

      MSVC 有 __declspec(noinline) 但它被记录为仅适用于成员函数。但是,我看到提到可能会阻止内联的“安全属性”?

      如果可能,强制将其他函数内联到这个函数中。

      __attribute__((flatten)) 添加到函数的任何声明中。

      请注意,noinline 比这更强大,其定义在编译时未知的函数也是如此。

      MSVC 似乎没有等效项。我见过一次提到[[msvc::forceinline_calls]](应用于语句或块),但它不是递归的。

      【讨论】:

      • 缺失:MSVC 呢?它有一些 C89 方言扩展,但我从不使用 MSVC,也不知道如何运行它的 nm 等效项。
      • 案例#4 void foo(void); inline void foo(void) { ... } 标题是“总是发射(对于一个TU,解决前面的问题)”。其他 4 个以“inline ....”开头。情况 4:它是否也内联
      • @chux-ReinstateMonica 内联更多的是关于符号行为而不是性能。无论关键字是否存在,编译器都允许进行内联以提高性能(取决于插入规则)。案例#4 的关键部分是它如何与案例#3 的未解析符号相关联;无论如何,通常没有(m)该TU中的任何呼叫者。
      • 我的评论更多地是关于为什么在案例 1、2、3、5 的标题描述中有 inline 的情况下这个答案,但 4 没有。您是否建议万一代码从不内联,总是内联,有时内联?案例#4 的内联可能性与其他 4 种不同吗?
      【解决方案5】:

      内联、静态内联和外部内联的情况很复杂,尤其是因为 gcc 和 C99 为它们的行为定义了稍微不同的含义(可能也是 C++)。您可以在 C here 中找到一些有用且详细的信息。

      【讨论】:

        【解决方案6】:

        宏是您的选择,而不是内联函数。宏统治内联函数的罕见情况。请尝试以下操作:我编写了这个“MACRO MAGIC”代码,它应该可以工作!在 gcc/g++ Ubuntu 10.04 上测试

        //(c) 2012 enthusiasticgeek (LOGGING example for StackOverflow)
        
        #ifdef __cplusplus
        
        #include <cstdio>
        #include <cstring>
        
        #else
        
        #include <stdio.h>
        #include <string.h>
        
        #endif
        
        //=========== MACRO MAGIC BEGINS ============
        
        //Trim full file path
        #define __SFILE__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/')+1 : __FILE__ )
        
        #define STRINGIFY_N(x) #x
        #define TOSTRING_N(x) STRINGIFY_N(x)
        #define _LINE (TOSTRING_N(__LINE__))
        
        #define LOG(x, s...) printf("(%s:%s:%s)"  x "\n" , __SFILE__, __func__, _LINE, ## s);
        
        //=========== MACRO MAGIC ENDS ============
        
        int main (int argc, char** argv) {
        
          LOG("Greetings StackOverflow! - from enthusiasticgeek\n");
        
          return 0;
        }
        

        对于多个文件,在单独的头文件中定义这些宏,包括在每个 c/cc/cxx/cpp 文件中的相同。请优先使用内联函数或 const 标识符(视情况而定)而不是宏尽可能。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-09-19
          • 2010-11-19
          • 2014-10-08
          • 2010-12-05
          • 2019-07-08
          • 1970-01-01
          • 2013-10-21
          • 1970-01-01
          相关资源
          最近更新 更多