The tricky inline specifier in C99
Try to compile the following simple C program in C99 mode with GCC:
inline void foo() {} int main(void) { foo(); }
The results may surprise you:
$ gcc test.c -std=c99 /tmp/ccWN4GRh.o: In function `main': test.c:(.text+0xa): undefined reference to `foo' collect2: ld returned 1 exit status
Huh? The function’s right there!
Well it turns out that this is not a bug in GCC, but a peculiarity in the way the inline
keyword is defined by the C99 standard. Basically, a function declared inline
without either of the extern
or static
linkage specifiers only creates an inline definition of that function, not an external definition.
When presented with a call to such a function, the compiler can choose to call either the inline definition or the external definition. If it chooses the external definition, and such an external definition doesn’t exist, we get a linker error, as in the above example. In the dry words of the standard:
ISO/IEC 9899:1999 ยง6.7.4/6:
Any function with internal linkage can be an inline function. For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.120)
So there are three ways to fix this code:
- Give
foo
internal linkage (by declaring itstatic
) and avoid the above clause entirely - Declare
foo
asextern
- Provide a separate external definition for
foo
in another translation unit (that is, another source file)
I would strongly recommend against solution #3, since then your code base will have two separate definitions of foo
, which will be very confusing for people reading your code. It’s very easy for them to get out of sync, if somebody changes one definition but forgets to change the other, which makes for some insidious bugs (such as working correctly in a debug build but not in a release build, or vice-versa).
If your inline function is defined and used only in one source file (as in our toy example), solution #1 is the way to go: just give it internal linkage. No reason to make it world-accessible.
Conversely, if your inline function is defined in a header file so it can be used throughout your code, it makes more sense to give it external linkage. Go with solution #2:
extern inline void foo() { /* body */ }