The (un)inuitive meaning of "inline" in C
+-------------------+ | + - - - - - + | | x I N L I N E y z | | + - - - - - + | +-------------------+ Effectively, the inline keyword in C turns a definition into a declaration, and serves to move the function body to the header file. Actual meaning of the inline keyword inline turns actual definitions pretty much into forward declarations. inline definitions are shared in the header and the declaration (indicating the fallback implementation) goes into the implementation file. The non-inline declaration implicitly reuses any other definition in the same file and therefore turns into the implementation. Conversion into inline functions Converting functions into inline functions is easy: move the body from the implementation file into the header file and replace the extern with the inline keyword at the destination. (But consider the annoying refactoring overhead which only makes sense for significant performance gains. If your function is never inlined or inline is undone later, it was in vain.) Before: // file.h int square(int x); // file.c #include "file.h" int square(int x) { return x*x; } After: // file.h inline int square(int x) { return x*x; } // file.c #include "file.h" extern inline int square(int x); // "extern inline" is optional The non-inline declaration doesn't even require an extern , because non-inline function declarations are extern by default. extern inline can also be used in the implemention file to emit a function from the definition. I believe, there is no effective compilation difference between extern inline and extern except for expressing with extern inline the intent that other coders should search the header file for the definition. (Not sure, if there is compiler-dependent semantics.) GCC seems to make no difference between them. But you need to turn optimization for GCC to inline functions. inline as assertion Without an extern declaration, the inline asserts inlining of the function, i.e. compilation is made to fail if the compiler doesn't freely choose to inline it. (E.g. it enforces compilation with optimization or fails ultimately when the function cannot be inlined.) Original problem Interesting is, what is the sense behind this specification of the C committee? (Besides technical limitations such as C's translation units.) It seems, the original problem was not to annotate functions as "inlinable". Functions are inlinable when the compiler chooses to inline them, independent of a keyword. But! It can only do so (in C) if it knows the definition. Since function declarations in the header lack a body, they cannot be inlined in foreign files. The only purpose of inline is to use a function body that does not emit a named function, allowing for a function body in the header file. inline rather means, a body is inlined in the declaration, rather than at the call sites. History Previously, the GCC compiler defined extern inline what later became inline in the standard. It made sense to think, that anything in headers should be extern. The C committee apparently had a different notion of extern, indicating that it is only type information and not the associated (function) definition (which could be the same or a different file). Extern declarations weren't even required before C23. Not providing them would just omit type checking for unknown functions (with warnings) and assume integer return type. Therefore, extern did not express that the function is in a different file (this would be assumed automatically) but provides typing for the current file (that is sufficiently distinct from a definition). And if we have a definition (the inline definition), we still need an actual signature (declaration) to automatically a emit a function in the current file. Using static inline Finally, there is also static inline. If your compiler really treats all functions with known body (and suitable preconditions) as inlinable, then a static inline function would be just a normal static function. After all, people likely use static inline when they believe to increase the inlining probability by doing so. (This maybe could depend on the compiler.) Unless you know that, it might not be worth using static inline. In return, there is a risk that every file which does not inline every call of it will create a copy of this function in that file. In big projects, this could lead to a lot of code duplication even with 90% inlining. A better probability for inlining is obtained for writing small pure functions. (Especially when passing compile-time known values to it.) In most recent GCC, static functions can silently be removed from the source code (with optimization turned on). In contrast, this does NOT happen with extern (including extern inline) declarations which always emit the function even if unused. This means, if your static inline header function is always inlined, then stat

+-------------------+
| + - - - - - + |
| x I N L I N E y z |
| + - - - - - + |
+-------------------+
Effectively, the inline
keyword in C turns a definition into a declaration, and serves to move the function body to the header file.
Actual meaning of the inline keyword
inline
turns actual definitions pretty much into forward declarations. inline
definitions are shared in the header and the declaration (indicating the fallback implementation) goes into the implementation file. The non-inline declaration implicitly reuses any other definition in the same file and therefore turns into the implementation.
Conversion into inline functions
Converting functions into inline
functions is easy: move the body from the implementation file into the header file and replace the extern
with the inline
keyword at the destination.
(But consider the annoying refactoring overhead which only makes sense for significant performance gains. If your function is never inlined or inline
is undone later, it was in vain.)
Before:
// file.h
int square(int x);
// file.c
#include "file.h"
int square(int x) { return x*x; }
After:
// file.h
inline int square(int x) { return x*x; }
// file.c
#include "file.h"
extern inline int square(int x); // "extern inline" is optional
The non-inline declaration doesn't even require an extern
, because non-inline function declarations are extern
by default. extern inline
can also be used in the implemention file to emit a function from the definition.
I believe, there is no effective compilation difference between extern inline
and extern
except for expressing with extern inline
the intent that other coders should search the header file for the definition. (Not sure, if there is compiler-dependent semantics.) GCC seems to make no difference between them. But you need to turn optimization for GCC to inline functions.
inline
as assertion
Without an extern declaration, the inline
asserts inlining of the function, i.e. compilation is made to fail if the compiler doesn't freely choose to inline it. (E.g. it enforces compilation with optimization or fails ultimately when the function cannot be inlined.)
Original problem
Interesting is, what is the sense behind this specification of the C committee? (Besides technical limitations such as C's translation units.)
It seems, the original problem was not to annotate functions as "inlinable". Functions are inlinable when the compiler chooses to inline them, independent of a keyword.
But! It can only do so (in C) if it knows the definition. Since function declarations in the header lack a body, they cannot be inlined in foreign files. The only purpose of inline
is to use a function body that does not emit a named function, allowing for a function body in the header file.
inline
rather means, a body is inlined in the declaration, rather than at the call sites.
History
Previously, the GCC compiler defined extern inline
what later became inline
in the standard. It made sense to think, that anything in headers should be extern
. The C committee apparently had a different notion of extern
, indicating that it is only type information and not the associated (function) definition (which could be the same or a different file).
Extern declarations weren't even required before C23. Not providing them would just omit type checking for unknown functions (with warnings) and assume integer return type.
Therefore, extern
did not express that the function is in a different file (this would be assumed automatically) but provides typing for the current file (that is sufficiently distinct from a definition). And if we have a definition (the inline
definition), we still need an actual signature (declaration) to automatically a emit a function in the current file.
Using static inline
Finally, there is also static inline
. If your compiler really treats all functions with known body (and suitable preconditions) as inlinable, then a static inline
function would be just a normal static
function.
After all, people likely use static inline
when they believe to increase the inlining probability by doing so. (This maybe could depend on the compiler.) Unless you know that, it might not be worth using static inline
. In return, there is a risk that every file which does not inline every call of it will create a copy of this function in that file. In big projects, this could lead to a lot of code duplication even with 90% inlining.
A better probability for inlining is obtained for writing small pure functions. (Especially when passing compile-time known values to it.)
In most recent GCC, static functions can silently be removed from the source code (with optimization turned on). In contrast, this does NOT happen with extern (including extern inline) declarations which always emit the function even if unused.
This means, if your static inline
header function is always inlined, then static inline
is as good as inline
or static
without an additional extern declaration. But if not, you get no linker error but code duplication in the linked binary, when using static
(or static inline
).
static inline
indeed ignores extern
declarations which follow after it but will not tolerate when they precede it.
Enforcing function inlining
And if you really need to enforce inlining, you need to use a compiler feature such as __attribute__((always_inline))
in GCC. It cannot inline every function (for example when a function pointer is used) and could fail to compile in such a case but it will try harder.
Anything to add or correct? Let me know.