11

If I have:

inline int foo(void)
{
   return  10 + 3;
}

int main(void)
{
   foo();
}

with GCC the file compiles well but the linker returns undefined reference to foo

Instead if I remove inline the linker is happy!

It seems as an identifier of an external definition is visible to the linker but an identifier of an inline definition is not.

Also if I compile with -O3 flag the linker sees the identifier of the inline definition.

What's the problem?

DaBler
  • 2,695
  • 2
  • 26
  • 46
xdevel2000
  • 20,780
  • 41
  • 129
  • 196
  • 3
    https://gcc.gnu.org/onlinedocs/gcc/Inline.html – Ashalynd Oct 22 '14 at 08:34
  • @Ashalynd the answer is probably in there somewhere, but what is it? – harold Oct 22 '14 at 08:36
  • I wonder whether the need for `extern` is GCC specific or part of some C standard. `inline` was added in C99 I believe, but I don't know how it affects linkage. – Kerrek SB Oct 22 '14 at 08:38
  • 1
    It compiles and links fine for me. What commandline are you using to compile and link? – interjay Oct 22 '14 at 08:46
  • 1
    @interjay I can reproduce it on my machine, but not on coliru. All I did was `g++ -std=c99 -x c main.cpp`. –  Oct 22 '14 at 08:50
  • 1
    The high optimization -o3 will result in the foo function being completely eliminated, including the call to it. The reason... foo returns 13 however, nothing is done with that 13, the optimizer will recognize these details, resulting is the complete elimination of foo and the call to foo. That is why compiling with -o3 works. – user3629249 Oct 22 '14 at 08:52
  • 2
    to get the compile to be successful, with no optimization, remember that an 'inline' function is not seen until it is inlined. So there needs to be a prototype statement for the foo function, before the foo function definition. – user3629249 Oct 22 '14 at 08:54
  • The GNU C compiler is by default using the C89 standard, with extensions. Since `inline` is a C99 feature using it with C89 is an extension, and apparently one that is not handled well. Might be a bug? – Some programmer dude Oct 22 '14 at 08:58
  • The problem with this code is that `inline` inside a .c is completely useless. Compilers do (or do not) inlining of code that the have hands on, anyhow, so there is no need for this usage. The keyword `inline` has been invented for situations where multiple translation units come into play and where you want to expose the definition of a function in a header file. – Jens Gustedt Oct 22 '14 at 09:50

4 Answers4

6

Okay, so after reading through VivienG's link, I think I've understood the exact reasoning behind this error message. It's confusing and misleading (at least to me; it shouldn't happen if you've got just one translation unit), yet it is possible to explain:

  • Assuming the compiler doesn't want to actually inline the code, it has to know where to put that function, especially when it's used in multiple translation units.

  • Classic approach is to create multiple copies, one for each translation unit (or at least for those units where it's used).

  • This may cause problems, e.g. when trying to do some function pointer comparisons (still leaves the question why you'd to that though).

To counter this (and other issues I possibly didn't list here), they've thought of some actually quite neat (although - as mentioned - in my opinion misleading) solution:

You declare the function as inline the way you know, but at the same time you tell the compiler where to put the non-inline version with the extern keyword.

So in your example, you'd keep your function as-is and put it in a header file (so it's known where it's going to be used):

inline int foo(void)
{
     return  10 + 3;
}

In addition, to tell the compiler where to place the non-inlined version, you'll have to add one more "forward" declaration in one translation unit:

extern inline int foo(void);

So the whole concept is essentially reversed when compared to classic functions: Put the implementation in the header and then a short declaration in just one file.

As mentioned already, while using the -O3 parameter, all code marked with inline is actually inlined, which won't cause the issue to happen.

Mario
  • 35,726
  • 5
  • 62
  • 78
  • Or the code work well if I put static, but I don't understand how this influence the linker... – xdevel2000 Oct 22 '14 at 10:15
  • Marking it `static` already tells the compiler "here and only here", so it once again knows where to put the function. – Mario Oct 22 '14 at 10:16
  • Yes, I can understand but you explain this always from the compiler view. However static is a linkage keyword that have a meaning for the linker. So, excuse me again, but this not explain why is the linker that reports the error... – xdevel2000 Oct 22 '14 at 10:33
  • 1
    In short, the compiler has to put that function into one of the translation units/object files, so the linker is able to find it. But since there's no indicator, where to put it, it ommits it, resulting in the misleading *undefined reference* error, since the linker can't find it anywhere (which is true). – Mario Oct 22 '14 at 10:51
2

You need to pass -std=gnu11 to your compiler.

gcc main. -o main -std=gnu11

EDIT: This post answer lots of questions.

VivienG
  • 2,143
  • 3
  • 24
  • 43
  • 2
    Care to explain the reason behind this? I don't see why the initial code should fail to compile with default parameters. – Mario Oct 22 '14 at 08:52
  • You can take a look at this [post](http://gustedt.wordpress.com/2010/11/29/myth-and-reality-about-inline-in-c99/) – VivienG Oct 22 '14 at 08:57
  • I think I've understood the reasons why it actuall fails with the given code above, but your link doesn't explain how/why this behavior changed with *gnu11* again. That isn't on the page or did I miss it? – Mario Oct 22 '14 at 09:23
  • -std=c99 or -std=c11 are better choices. Inlining was introduced in C99. – Lundin Oct 22 '14 at 09:53
2

In the C language from C99, if you want an inline function to be used in one compilation unit only, you should declare it as "static inline" and everything will be fine. Otherwise, an inline function that is declared without "static" must be declared as "extern inline" in exactly one compilation unit.

Usually, you will either use "static inline" in a .c file, or you will use "inline" in a .h file and declare the function as "extern inline" in exactly one .c file.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
1

I think user3629249's comment hit the nail on the head. If you compile the code with -O3, the call to foo() is eliminated. i.e., assembly difference:

With -O3:

main:
.LFB4:
    .cfi_startproc
    xorl    %eax, %eax
    ret
    .cfi_endproc

Without -O3:

main:
.LFB1:
    // ...
    call    foo
    // ...

The C standard says in footnote 161 (§6.9/5):

Thus, if an identifier declared with external linkage is not used in an expression, there need be no external definition for it.

A function at file scope has external linkage but a function declared inline without the extern keyword is an inline definition. In §6.9/5 it says:

An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.161)

4 [...] As discussed in 6.7, a declaration that also causes storage to be reserved for an object or a function named by the identifier is a definition.

i.e., using the storage class specifiers static or extern.

  • So an external definition acts as declaration too and the linker works well... but an inline definition doesn't... – xdevel2000 Oct 22 '14 at 10:16