简体   繁体   English

外部内联做什么?

[英]What does extern inline do?

I understand that inline by itself is a suggestion to the compiler, and at its discretion it may or may not inline the function, and it will also produce linkable object code.我知道inline本身是对编译器的建议,它可能会或可能不会内联 function,它还会生成可链接的 object 代码。

I think that static inline does the same (may or may not inline) but will not produce linkable object code when inlined (since no other module could link to it).我认为static inline执行相同的操作(可能内联也可能不内联),但内联时不会产生可链接的 object 代码(因为没有其他模块可以链接到它)。

Where does extern inline fit into the picture? extern inline在哪里适合图片?

Assume I want to replace a preprocessor macro by an inline function and require that this function gets inlined (eg, because it uses the __FILE__ and __LINE__ macros which should resolve for the caller but not this called function).假设我想用内联 function 替换预处理器宏,并要求这个 function 被内联(例如,因为它使用__FILE____LINE__宏,它们应该为调用者而不是这个被调用函数解析)。 That is, I want to see a compiler or linker error in case the function does not get inlined.也就是说,如果 function 没有内联,我想查看编译器或 linker 错误。 Does extern inline do this? extern inline会这样做吗? (I assume that, if it does not, there is no way to achieve this behavior other than sticking with a macro.) (我认为,如果没有,除了坚持使用宏之外,没有其他方法可以实现这种行为。)

Are there differences between C++ and C? C++和C有区别吗?

Are there differences between different compiler vendors and versions?不同的编译器供应商和版本之间是否存在差异?

in K&R C or C89, inline was not part of the language.在 K&R C 或 C89 中,内联不是语言的一部分。 Many compilers implemented it as an extension, but there were no defined semantics regarding how it worked.许多编译器将其实现为扩展,但没有关于它如何工作的定义语义。 GCC was among the first to implement inlining, and introduced the inline , static inline , and extern inline constructs; GCC 是最早实现内联的,并引入了inlinestatic inlineextern inline结构; most pre-C99 compiler generally follow its lead.大多数 C99 之前的编译器通常都跟随它的步伐。

GNU89: GNU89:

  • inline : the function may be inlined (it's just a hint though). inline :该函数可能是内联的(不过这只是一个提示)。 An out-of-line version is always emitted and externally visible.外部版本总是发出并且外部可见。 Hence you can only have such an inline defined in one compilation unit, and every other one needs to see it as an out-of-line function (or you'll get duplicate symbols at link time).因此,您只能在一个编译单元中定义这样的内联,而其他每个编译单元都需要将其视为外联函数(否则您将在链接时得到重复的符号)。
  • extern inline will not generate an out-of-line version, but might call one (which you therefore must define in some other compilation unit. The one-definition rule applies, though; the out-of-line version must have the same code as the inline offered here, in case the compiler calls that instead. extern inline不会生成外extern inline版本,但可能会调用一个(因此您必须在其他编译单元中定义它。不过,一个定义规则适用;外联版本必须具有相同的代码作为这里提供的内联,以防编译器调用它。
  • static inline will not generate a externally visible out-of-line version, though it might generate a file static one. static inline不会生成外部可见的外部版本,但它可能会生成文件静态版本。 The one-definition rule does not apply, since there is never an emitted external symbol nor a call to one.一个定义规则不适用,因为从来没有发出的外部符号,也没有对一个的调用。

C99 (or GNU99): C99(或 GNU99):

  • inline : like GNU89 "extern inline"; inline : 像 GNU89 "extern inline"; no externally visible function is emitted, but one might be called and so must exist不发出任何外部可见的函数,但可能会被调用,因此必须存在
  • extern inline : like GNU89 "inline": externally visible code is emitted, so at most one translation unit can use this. extern inline : 就像 GNU89 "inline": 发出外部可见的代码,所以最多一个翻译单元可以使用它。
  • static inline : like GNU89 "static inline". static inline :像 GNU89“静态内联”。 This is the only portable one between gnu89 and c99这是 gnu89 和 c99 之间唯一可移植的

C++: C++:

A function that is inline anywhere must be inline everywhere, with the same definition.在任何地方内联的函数必须在任何地方内联,并具有相同的定义。 The compiler/linker will sort out multiple instances of the symbol.编译器/链接器将整理出符号的多个实例。 There is no definition of static inline or extern inline , though many compilers have them (typically following the gnu89 model).没有static inlineextern inline定义,尽管许多编译器都有它们(通常遵循 gnu89 模型)。

I believe you misunderstand __FILE__ and __LINE__ based on this statement:我相信您根据以下声明误解了 __FILE__ 和 __LINE__:

because it uses the __FILE__ and __LINE__ macros which should resolve for the caller but not this called function因为它使用 __FILE__ 和 __LINE__ 宏,它们应该为调用者解析,而不是这个被调用的函数

There are several phases of compilation, and preprocessing is the first.编译有几个阶段,预处理是第一个。 __FILE__ and __LINE__ are replaced during that phase. __FILE__ 和 __LINE__ 在该阶段被替换。 So by the time the compiler can consider the function for inlining they have already been replaced.所以当编译器可以考虑用于内联的函数时,它们已经被替换了。

It sounds like you're trying to write something like this:听起来你正在尝试写这样的东西:

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

{
...
  printLocation();
...
  printLocation();
...
  printLocation();

and hoping that you'll get different values printed each time.并希望您每次都能打印出不同的值。 As Don says, you won't, because __FILE__ and __LINE__ are implemented by the preprocessor, but inline is implemented by the compiler.正如唐所说,你不会,因为 __FILE__ 和 __LINE__ 是由预处理器实现的,但内联是由编译器实现的。 So wherever you call printLocation from, you'll get the same result.因此,无论您从何处调用 printLocation,您都会得到相同的结果。

The only way you can get this to work is to make printLocation a macro.让它工作的唯一方法是使 printLocation 成为一个宏。 (Yes, I know...) (是的,我知道...)

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

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...

Instead of answering "what does it do?", I'm answering "how do I make it do what I want?"我不是回答“它有什么作用?”,而是回答“我如何让它做我想做的事?” There are 5 kinds of inlining, all available in GNU C89, standard C99, and C++.有 5 种内联,在 GNU C89、标准 C99 和 C++ 中都可用。 MSVC has some of them (note that I haven't tested the MSVC code) MSVC 有一些(请注意,我还没有测试 MSVC 代码)

always inline, unless the address is taken总是内联,除非地址被占用

Add __attribute__((always_inline)) to any declaration, then use one of the below cases to handle the possibility of its address being taken.__attribute__((always_inline))添加到任何声明中,然后使用以下情况之一来处理其地址被占用的可能性。

You should probably never use this, unless you need its semantics (eg to affect the assembly in a certain way, or to use alloca ).您可能永远不应该使用它,除非您需要它的语义(例如,以某种方式影响程序集,或使用alloca )。 The compiler usually knows better than you whether it's worth it.编译器通常比你更清楚它是否值得。

MSVC has __forceinline which appears mostly the same, but apparently it refuses to inline in quite a few common circumstances (eg when optimization is off) where other compilers manage just fine. MSVC 有__forceinline ,它看起来大致相同,但显然它拒绝在其他编译器管理得很好的一些常见情况下(例如,当优化关闭时)内联。

inline and emit a weak symbol (like C++, aka "just make it work")内联并发出弱符号(如 C++,又名“让它工作”)

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

Note that this leaves a bunch of copies of the same code lying around, and the linker picks one arbitrarily.请注意,这会留下一堆相同代码的副本,链接器会任意选择一个。

MSVC doesn't appear to have an exact equivalent in C mode, although there are a couple of similar things. MSVC 在 C 模式下似乎没有完全等效的东西,尽管有一些类似的东西。 __declspec(selectany) appears to be talking about data only, so might not apply to functions? __declspec(selectany)似乎只讨论数据,所以可能不适用于函数? There is also linker support for weak aliases , but does that work here?还有对弱别名的链接器支持,但这在这里有效吗?

inline, but never emit any symbol (leaving external references)内联,但从不发出任何符号(留下外部引用)

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

MSVC's __declspec(dllimport) , combined with an actual definition (otherwise unusual), supposedly does this. MSVC 的__declspec(dllimport)结合实际定义(否则不寻常),据说可以做到这一点。

emit always (for one TU, to resolve the preceding)总是发出(对于一个 TU,解决前面的问题)

The hinted version emits a weak symbol in C++, but a strong symbol in either dialect of C:暗示版本在 C++ 中发出一个弱符号,但在 C 的任一方言中发出一个强符号:

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

Or you can do it without the hint, which emits a strong symbol in both languages:或者,您可以在没有提示的情况下执行此操作,这会在两种语言中发出强符号:

void foo(void) { ... }

Generally, you know what language your TU is when you're providing the definitions, and probably don't need much inlining.通常,当您提供定义时,您知道您的 TU 是什么语言,并且可能不需要太多内联。

MSVC's __declspec(dllexport) supposedly does this. MSVC 的__declspec(dllexport)据说可以做到这一点。

inline and emit in every TU内联并在每个 TU 中发出

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

For all of these except the static one, you can add a void foo(void) declaration above.对于除static之外的所有这些,您可以在上面添加一个void foo(void)声明。 This helps with the "best practice" of writing clean headers, then #include ing a separate file with the inline definitions.这有助于编写干净的标题的“最佳实践”,然后#include使用内联定义单独的文件。 Then, if using C-style inlines, #define some macro differently in one dedicated TU to provide the out-of-line definitions.然后,如果使用 C 风格的内联,在一个专用 TU 中#define一些不同的宏以提供外联定义。

Don't forget extern "C" if the header might be used from both C and C++!如果头文件可以从 C 和 C++ 中使用,请不要忘记extern "C"


There are also a couple of related things:还有一些相关的事情:

never inline从不内联

Add __attribute__((noinline)) to any declaration of the function.__attribute__((noinline))到函数的任何声明中。

MSVC has __declspec(noinline) but it is documented to only work for member functions. MSVC 有__declspec(noinline)但它被记录为仅适用于成员函数。 However, I've seen mention of "security attributes" which might prevent inlining?但是,我已经看到提到可能会阻止内联的“安全属性”?

force other functions to be inlined into this one if possible.如果可能,强制其他函数内联到这个函数中。

Add __attribute__((flatten)) to any declaration of the function.__attribute__((flatten))到函数的任何声明中。

Note that noinline is more powerful than this, as are functions whose definition isn't known at compile-time.请注意, noinline比这更强大,其定义在编译时未知的函数也是如此。

MSVC doesn't appear to have an equivalent. MSVC 似乎没有等价物。 I've seen a single mention of [[msvc::forceinline_calls]] (applied to a statement or block), but it's not recursive.我见过一次提到[[msvc::forceinline_calls]] (应用于语句或块),但它不是递归的。

Macros are your choice here rather than the inline functions.宏是您的选择,而不是内联函数。 A rare occasion where macros rule over inline functions.宏统治内联函数的罕见情况。 Try the following: I wrote this "MACRO MAGIC" code and it should work!尝试以下操作:我写了这个“MACRO MAGIC”代码,它应该可以工作! Tested on gcc/g++ Ubuntu 10.04在 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;
}

For multiple files define these macros in a separate header file including the same in each c/cc/cxx/cpp files.对于多个文件,在单独的头文件中定义这些宏,包括每个 c/cc/cxx/cpp 文件中的相同内容。 Please prefer inline functions or const identifiers (as the case demands) over macros wherever possible.请尽可能使用内联函数或常量标识符(视情况而定)而不是宏。

The situation with inline, static inline and extern inline is complicated, not least because gcc and C99 define slightly different meanings for their behavior (and presumably C++, as well).内联、静态内联和外联内联的情况很复杂,尤其是因为 gcc 和 C99 为其行为定义了略有不同的含义(大概也是 C++)。 You can find some useful and detailed information about what they do in C here .您可以在此处找到有关它们在 C 中的作用的一些有用且详细的信息。

C++ only:仅限 C++:

As others have pointed out, macros (here __FILE__ and __LINE__ ) are evaluated before compiling and linking;正如其他人指出的那样,宏(此处为__FILE____LINE__ )在编译和链接之前进行评估; So if you have a function that uses those and you want them to be different for each file, you need the opposite of inline .因此,如果您有一个使用它们的 function 并且您希望它们对于每个文件都不同,那么您需要与inline相反。 Since the __FILE__ and __LINE__ values are going to be different for each file, then the definition (body) of the function is going to be different for each file.由于每个文件的__FILE____LINE__值将不同,因此每个文件的 function 的定义(主体)将不同。 But (non-static) inline means that if the function is defined in multiple translation units, the all must have the same definition.但是(非静态) inline意味着如果 function 在多个翻译单元中定义,则所有翻译单元必须具有相同的定义。

You could define (not declare) a normal function or static or static inline function in a header file and include it anywhere you want. You could define (not declare) a normal function or static or static inline function in a header file and include it anywhere you want. This way each translation unit (source file) gets its own copy of the function with different __FILE__ and __LINE__ values.这样,每个翻译单元(源文件)都会获得自己的 function 副本,其中__FILE____LINE__值不同。 Although, I think in the case of static inline , the inline keyword is useless in most cases.虽然,我认为在static inline的情况下, inline关键字在大多数情况下是无用的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM