Practical details and example for C inline
without static
inline int foo(int a) { return a+1; } // in foo.h, no extern
This gives the compiler a definition it can inline if it wants, for any .c
that includes this header.
If optimization is disabled or the function is huge, it won't inline. Or taking a function pointers that doesn't optimize away.
When it doesn't inline, the resulting .o
/ .obj
will contain a reference to the symbol name for foo()
(e.g. _foo
or foo
) exactly like for normal functions that you didn't declare as inline
. The linker will fill in an address into the machine code when it finds a definition for the symbol.
But (unlike C++) it also won't create a stand-alone callable asm definition of the function in the .o
/ .obj
compiled from a .c
with call-sites where it decided not to inline. There won't be a foo
definition in the symbol table of every .o
, or in fact any of them if you don't tell it to make one.
(C++ does requires redundant code-gen and for the linker to discard duplicate definitions, instead of requiring manual instantiation.)
For your program to link, exactly one .c
(aka translation unit / compilation unit) needs to contain an extern inline
declaration that instantiates a non-inline copy of the function into a .o
. Example .c
file:
#include "foo.h" // Let the compiler see the definition
extern inline int foo(int); // and instantiate in exactly one .c
// or
// extern inline int foo(); // Without repeating the args is also valid
// until C23 makes () equivalent to (void)
It has external linkage, so calls from multiple .c
files can use this definition. Unlike static inline
where each .c
gets its own copy of the asm for any call sites that don't inline, unless the linker is clever about identical code folding.
If you don't do this, you can get a link error even from a single-file program:
// static // static inline can't create link errors
inline int foo(int a) { return a+1; } // would normally be in a separate header
int main(void){
return foo(-1);
}
//extern inline int foo(int); // link error without this
As we can see on the Godbolt compiler explorer with GCC's default of no optimization (-O0
), the compiler doesn't inline any methods (even though they have the inline qualifier), and the asm output doesn't include a foo:
label, but main
includes a call foo
. With either "link to binary" or "execute the code" options, ld
gives an error:
/tmp/ccNB76SP.o: in function `main':
<source>:4: undefined reference to `foo'
Uncommenting the extern inline int foo(int);
lets it link and run, and we can see the asm output from this .c
does now include a foo:
definition of the function.
(The "filter directives" option is on so we don't see the .global foo
which makes this symbol visible in the .o
for other files to link against. See also How to remove "noise" from GCC/clang assembly output?)
With optimization enabled (-O2
or higher), the compiler will (normally) have inlined every call so there won't be any reference in the asm to its symbol name. So your program would link even without the extern inline
declaration / instantiation. (As shown in another pane of the Godbolt link above.) Like with undefined behaviour, practical results of violating the one-definition rule can depend on compiler options like optimization level.
Another option is to use static inline
in the header. If all the call-sites do inline, it wouldn't have any downside like duplicate copies of the same machine code bloating your file size and I-cache footprint. Other than linker details, it's equivalent unless your function uses static
local variables that need to be shared program-wide by one instead of the function, not private to each .c
.
// per-translation-unit (static inline) vs. per-program (inline / extern inline)
// matters for functions like this, unlike for most functions
static inline unsigned sequence_number() {
static unsigned counter = 0; // because each .c has its own copy of this var
return ++counter;
}
Even in optimized builds you could end up with non-inline calls for big functions, or if you take the address of the inline
function and pass the function pointer to another function (which doesn't also get inlined, e.g. because it's big or you didn't use link-time optimization (LTO)). Or anything else that results in a call from a call-site with a runtime-variable function pointer that compile-time constant folding can't resolve to a single possible target.
Normally avoid inline
for big functions, but a function could perhaps become big from itself inlining a couple other functions. The compiler might decide to make a non-inlined (aka outlined) definition that inlines both its children, or might inline this function into callers and maybe or maybe not inline one or both of the callees in this hypothetical example.
Related Q&As
.h
and theextern inline
declaration in exactly one.c
to instantiate a non-inline definition and make a working C program with a non-static
inline
function defined in a header and thus able to actually inline, but still link if it doesn't. In C, should inline functions in headers be externed in the .c file? is also about that, with a concise answer. – Cusk