The program is valid as written, but def.c
is required to ensure the code always works with all compilers and any combination of optimisation levels for the different files.
Because there is a declaration with extern
on it, def.c
provides an external definition of the function foo()
, which you can confirm with nm
:
$ nm def.o
0000000000000000 T foo
That definition will always be present in def.o
no matter how that file is compiled.
In use.c
there is an inline definition of foo()
, but according to 6.7.4 in the C standard it is unspecified whether the call to foo()
uses that inline definition or uses an external definition (in practice whether it uses the inline definition depends on whether the file is optimised or not). If the compiler chooses to use the inline definition it will work. If it chooses not to use the inline definition (e.g. because it is compiled without optimisations) then you need an external definition in some other file.
Without optimisation use.o
has an undefined reference:
$ gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c
$ nm use.o
0000000000000000 T bar
U foo
But with optimisation it doesn't:
$ gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c -O3
$ nm use.o
0000000000000000 T bar
In main.cpp
there will be a definition of foo()
but it will typically generate a weak symbol, so it might not be kept by the linker if another definition is found in another object. If the weak symbol exists, it can satisfy any possible reference in use.o
that requires an external definition, but if the compiler inlines foo()
in main.o
then it might not emit any definition of foo()
in main.o
, and so the definition in def.o
would still be needed to satisfy use.o
Without optimisation main.o
contains a weak symbol:
$ g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp
$ nm main.o
U bar
0000000000000000 W foo
0000000000000000 T main
U printf
However compiling main.cpp
with -O3
inlines the call to foo
and the compiler does not emit any symbol for it:
$ g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp -O3
$ nm main.o
U bar
0000000000000000 T main
U printf
So if foo()
is not inlined in use.o
but is inlined in main.o
then you need the external definition in def.o
Would it work if def.c was removed and foo was not used in C?
Yes. If foo
is only used in the C++ file then you do not need the external definition of foo
in def.o
because main.o
either contains its own (weak) definition or will inline the function. The definition in foo.o
is only needed to satisfy non-inlined calls to foo
from other C code.
Aside: the C++ compiler is allowed to skip generating any symbol for foo
when optimising main.o
because the C++ standard says that a function declared inline
in one translation unit must be declared inline in all translation units, and to call a function declared inline
the definition must be available in the same file as the call. That means the compiler knows that if some other file wants to call foo()
then that other file must contain the definition of foo()
, and so when that other file is compiled the compiler will be able to generate another weak symbol definition of the function (or inline it) as needed. So there is no need to output foo
in main.o
if all the calls in main.o
have been inlined.
These are differnet semantics from C, where the inline definition in use.c
might be ignored by the compiler, and the external definition in def.o
must exist even if nothing in def.c
calls it.
inline
keyword? I thought they spell it differently. – Catechizeinline
was standardised. – Einbergerfoo()
then makingfoo
static means you get a different static local in every translation unit, not a single one for the whole program). Usestatic
where it makes sense, not just to solve linker errors. – Billyebilobate