Inline (C and C++)


In the C and C++ programming languages, an inline function is one qualified with the keyword inline; this serves two purposes:
  1. It serves as a compiler directive that suggests that the compiler substitute the body of the function inline by performing inline expansion, i.e. by inserting the function code at the address of each function call, thereby saving the overhead of a function call. In this respect it is analogous to the register storage class specifier, which similarly provides an optimization hint.
  2. The second purpose of inline is to change linkage behavior; the details of this are complicated. This is necessary due to the C/C++ separate compilation + linkage model, specifically because the definition of the function must be duplicated in all translation units where it is used, to allow inlining during compiling, which, if the function has external linkage, causes a collision during linking. C and C++ resolve this in different ways.

    Example

An inline function can be written in C or C++ like this:

inline void swap

Then, a statement such as the following:

swap;

may be translated into :

int tmp = x;
x = y;
y = tmp;

When implementing a sorting algorithm doing lots of swaps, this can increase the execution speed.

Standard support

and C99, but not its predecessors K&R C and C89, have support for inline functions, though with different semantics. In both cases, inline does not force inlining; the compiler is free to choose not to inline the function at all, or only in some cases. Different compilers vary in how complex a function they can manage to inline. Mainstream C++ compilers like Microsoft Visual C++ and GCC support an option that lets the compilers automatically inline any suitable function, even those not marked as inline functions. However, simply omitting the inline keyword to let the compiler make all inlining decisions is not possible, since the linker will then complain about duplicate definitions in different translation units. This is because inline not only gives the compiler a hint that the function should be inlined, it also has an effect on whether the compiler will generate a callable out-of-line copy of the function.

Nonstandard extensions

, as part of the dialect gnu89 that it offers, has support for inline as an extension to C89. However, the semantics differ from both those of C++ and C99. armcc in C90 mode also offers inline as a non-standard extension, with semantics different from gnu89 and C99.
Some implementations provide a means by which to force the compiler to inline a function, usually by means of implementation-specific declaration specifiers:
  • Microsoft Visual C++: __forceinline
  • gcc or clang: __attribute__) or __attribute__), the latter of which is useful to avoid a conflict with a user-defined macro named always_inline.
Indiscriminate uses of that can result in larger code, minimal or no performance gain, and in some cases even a loss in performance. Moreover, the compiler cannot inline the function in all circumstances, even when inlining is forced; in this case both gcc and Visual C++ generate warnings.
Forcing inlining is useful if:
  • inline is not respected by the compiler
  • inlining results is necessary for boosting performance
For code portability, the following preprocessor directives may be used:

  1. ifdef _MSC_VER
#define forceinline __forceinline
  1. elif defined
#define forceinline inline __attribute__)
  1. elif defined
#if __has_attribute
#define forceinline inline __attribute__)
#else
#define forceinline inline
#endif
  1. else
#define forceinline inline
  1. endif

Storage classes of inline functions

static inline has the same effects in all C dialects and C++. It will emit a locally visible function if required.
Regardless of the storage class, the compiler can ignore the inline qualifier and generate a function call in all C dialects and C++.
The effect of the storage class extern when applied or not applied to inline functions differs between the C dialects and C++.

C99

In C99, a function defined inline will never, and a function defined extern inline will always, emit an externally visible function. Unlike in C++, there is no way to ask for an externally visible function shared among translation units to be emitted only if required.
If inline declarations are mixed with extern inline declarations or with unqualified declarations, the translation unit must contain a definition and an externally visible function will be emitted for it.
A function defined inline requires exactly one function with that name somewhere else in the program which is either defined extern inline or without qualifier. If more than one such definition is provided in the whole program, the linker will complain about duplicate symbols. If, however, it is lacking, the linker does not necessarily complain, because, if all uses could be inlined, it is not needed. But it may complain, since the compiler can always ignore the inline qualifier and generate calls to the function instead, as typically happens if the code is compiled without optimization. A convenient way is to define the inline functions in header files and create one.c file per function, containing an extern inline declaration for it and including the respective header file with the definition. It does not matter whether the declaration is before or after the include.
To prevent unreachable code from being added to the final executable if all uses of a function were inlined, it is advised to put the object files of all such.c files with a single extern inline function into a static library file, typically with ar rcs, then link against that library instead of the individual object files. That causes only those object files to be linked that are actually needed, in contrast to linking the object files directly, which causes them to be always included in the executable. However, the library file must be specified after all the other object files on the linker command line, since calls from object files specified after the library file to the functions will not be considered by the linker. Calls from inline functions to other inline functions will be resolved by the linker automatically.
An alternative solution is to use link time optimization instead of a library. gcc provides the flag -Wl,--gc-sections to omit sections in which all functions are unused. This will be the case for object files containing the code of a single unused extern inline function. However, it also removes any and all other unused sections from all other object files, not just those related to unused extern inline functions. With this approach, it is also possible to use a single.c file with all extern inline functions instead of one.c file per function. Then the file has to be compiled with -fdata-sections -ffunction-sections. However, the gcc manual page warns about that, saying "Only use these options when there are significant benefits from doing so."
Some recommend an entirely different approach, which is to define functions as static inline instead of inline in header files. Then, no unreachable code will be generated. However, this approach has a drawback in the opposite case: Duplicate code will be generated if the function could not be inlined in more than one translation unit. The emitted function code cannot be shared among translation units because it must have different addresses. This is another drawback; taking the address of such a function defined as static inline in a header file will yield different values in different translation units. Therefore, static inline functions should only be used if they are used in only one translation unit, which means that they should only go to the respective.c file, not to a header file.

gnu89

gnu89 semantics of inline and extern inline are essentially the exact opposite of those in C99, with the exception that gnu89 permits redefinition of an extern inline function as an unqualified function, while C99 inline does not. Thus, gnu89 extern inline without redefinition is like C99 inline, and gnu89 inline is like C99 extern inline; in other words, in gnu89, a function defined inline will always and a function defined extern inline will never emit an externally visible function. The rationale for this is that it matches variables, for which storage will never be reserved if defined as extern and always if defined without. The rationale for C99, in contrast, is that it would be astonishing if using inline would have a side-effect—to always emit a non-inlined version of the function—that is contrary to what its name suggests.
The remarks for C99 about the need to provide exactly one externally visible function instance for inlined functions and about the resulting problem with unreachable code apply mutatis mutandis to gnu89 as well.
gcc up to and including version 4.2 used gnu89 inline semantics even when -std=c99 was explicitly specified. With version 5, gcc switched from gnu89 to the gnu11 dialect, effectively enabling C99 inline semantics by default. To use gnu89 semantics instead, they have to be enabled explicitly, either with -std=gnu89 or, to only affect inlining, -fgnu89-inline, or by adding the gnu_inline attribute to all inline declarations. To ensure C99 semantics, either -std=c99, -std=c11, -std=gnu99 or -std=gnu11 can be used.