Will this code result in undefined behavior?
header.h
#ifdef __cplusplus
extern "C"
{
#endif
inline int foo(int a)
{
return a * 2;
}
#ifdef __cplusplus
}
#endif
def.c
#include "header.h"
extern inline int foo(int a);
use.c
#include "header.h"
int bar(int a)
{
return foo(a + 3);
}
main.cpp
#include <stdio.h>
#include "header.h"
extern "C"
{
int bar(int a);
}
int main(int argc, char** argv)
{
printf("%d\n", foo(argc));
printf("%d\n", bar(argc));
}
This is a example of a program where an inline
function has to be used in both C and C++. Would it work if def.c
was removed and foo
was not used in C? (This is assuming that the C compiler is C99.)
This code works when compiled with:
gcc -std=c99 -pedantic -Wall -Wextra -c -o def.o def.c
g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp
gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c
g++ -std=c++11 -pedantic -Wall -Wextra -o extern_C_inline def.o main.o use.o
foo
is only in extern_C_inline
once because the different versions that the compiler outputs in different object files get merged, but I would like to know if this behavior is specified by the standard. If I remove extern
definition of foo
and make it static
then foo
will appear in the extern_C_inline
multiple times because the compiler outputs it in each compilation unit.
Similarly, if you define a function as extern inline , or redeclare an inline function as extern , the function simply becomes a regular, external function and is not inlined. End of C only. Beginning of C++ only.
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.
Standard supportC++ 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.
Inline functions are more than a simple copy-paste procedure (in contrast to, say, preprocessor macros). They behave like normal functions, so any return value would be reflected to the caller just like a normal function.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With