clinkerinlineextern

When inline function definition and extern function declaration are all present in one .c file, no link error is observed


Even though this might look like duplication, I'd claim that it isn't.

Why does a compiler make the Foo function, which is defined in foo.c as inline double Foo() { return 1.2; }, a global function if the foo.c file also has the extern double Foo(); line anywhere?

Please see the example with foo.c and main.c:

// foo.c
#if defined(WITH_DECL)
extern double Foo();
#endif

inline double Foo() {
  return 1.2;
}

double Bar() {
  printf("%lf\n", Foo());
  return Foo() + Foo();
}

And,here's main.c:

double Foo();
double Bar();
int main() {
  printf("Bar + Foo = %lf + %lf\n", Bar(), Foo());
  return 0;
}

Here's how I built:

clang -O2 main.c foo.c
clang -O2 main.c foo.c -DWITH_DECL

The first command failed. The second command silently succeeded.

I understand why the first failed. As expected it's a linker error:

clang -g3 -O0 -std=c11 foo.c main.c -Wall -Werror -O2
/usr/bin/x86_64-pc-linux-gnu-ld.bfd: /usr/bin/x86_64-pc-linux-gnu-ld.bfd: DWARF error: mangled line number section (bad file number)
/tmp/main-10e175.o: in function `main':
/home/aion1223/workspace/prac/main.c:7:(.text+0x8): undefined reference to `Foo'
clang: error: linker command failed with exit code 1 (use -v to see invocation)`

I do not understand why the second command does not fail.

First, do I expect this to happen for all standard-complying compilers? Second, why at least some of my compilers silently build the executable when -DWITH_DECL is given?

I'd like to figure out if this is something that I can rely on, or it just happened so but is not guaranteed by C language standard.

The standard I tried is c99, c11, and c17. The compilers are clang 20, gcc14, and gcc12.


Solution

  • Starting with C99, if a function is defined inline without any extern or static declarations, it is an inline definition. This means it can't be used outside of the current translation unit.

    By adding an extern declaration (which doesn't actually require the extern keyword), it becomes an external definition that can be used outside of the translation unit.

    This is spelled out in section 6.7.5p7 of the C standard:

    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 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. Inline definitions provide an alternative to external definitions, 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.

    So to summarize, what you're seeing is expected for a compiler in C99 mode or later.

    On a side note, some compilers won't include an inline definition at all and will generate link errors in the translation unit it was defined in unless certain options are passed in. For example, gcc will generate linker errors for references in foo.c with -O0 but not -O1 or higher.