3

If I try to compile the following C code without declaring the function static, I get a linker error:

undefined reference to '_fun'

but it works if I don't make it static. In c++, it works just fine without the static keyword.

// Doesn't work
inline int fun()
{
    return 3;
}

// Works
static inline int fun()
{
    return 3;
}

int main(int argc, const char * argv[])
{
    printf("%i", fun());
}
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Stas Jaro
  • 4,747
  • 5
  • 31
  • 53
  • Worked fine on gcc version 4.7. – unxnut Jul 03 '13 at 01:58
  • What's the error message? (It works for me with gcc, but not with "gcc -std=c99 -pedantic".) – Keith Thompson Jul 03 '13 at 01:58
  • @KeithThompson: `undefined reference to '_fun'` - do you know what's causing this? – Karoly Horvath Jul 03 '13 at 02:01
  • 1
    In C++, it works because `inline` effectively changes the linkage of the function, without actually changing it, by requiring a definition in each translation unit that uses the function. I suspect that C99 does things differently and `inline extern` is a useless combination, because multiple definitions would be simultaneously required and forbidden. – Potatoswatter Jul 03 '13 at 02:05
  • It's a *linking error*, as opposed to a *compile error*. I get this in Clang (and in GCC with C99 mode). I also only get this in C, not C++. For some reason, the compiler isn't inlining `fun()` and so the linker doesn't see it when needed in `main()`. I'm not sure why it's doing this though. – chrisaycock Jul 03 '13 at 02:09
  • possible duplicate of [Is "inline" without "static" or "extern" ever useful in C99?](http://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99) – Jonathan Leffler Jul 03 '13 at 02:23
  • see also http://stackoverflow.com/questions/16245521/c99-inline-function-in-c-file – Christoph Jul 03 '13 at 06:56

2 Answers2

3

The requirements for inline in C are defined by section 6.7.4 of the ISO C standard. Quoting this section from the N1256

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.

As far as I can tell, your definition satisfies all those requirements. This:

inline int fun()
{
    return 3;
}

is both a declaration and a definition of fun. Without the inline keyword, it would have external linkage.

The tricky part is the last sentence:

It is unspecified whether a call to the function uses the inline definition or the external definition.

In this case, there is no external definition. You don't say what compiler you're using, but gcc -std=c99 -pedantic apparently chooses, by default, to use the external definition, and since there isn't one, you get a linker error. (Without -std=c99 -pedantic, there's no linker error, but that's because gcc also implements inline as an extension on top of C90.)

If you're only going to be using the function in that one source file, you might as well add the static keyword anyway, to give it internal linkage.

And experiment shows that your program compiles, links, and runs correctly if I compile it with optimization, using any of -O1, -O2, or -O3.

A footnote in the C standard seems to imply that gcc is behaving correctly. An example in the same section has a similar non-static inline function definition:

inline double cels(double t)
{
      return (5.0 * (t - 32.0)) / 9.0;
}

followed by:

Because cels has external linkage and is referenced, an external definition has to appear in another translation unit (see 6.9); the inline definition and the external definition are distinct and either may be used for the call.

The Standard's intention seems to be that if an inline function has internal linkage, it should be defined just once in the source file that uses it, but if it has external linkage, the inline definition is an alternative to a non-inline definition that must appear elsewhere. The choice of whether to call the external function or expand the inline definition is left to the whim of the compiler.

Some points not directly relevant to your question:

int fun() should probably be int fun(void). The empty parentheses are legal, but they indicate that the function takes an unspecified number and type(s) of arguments. The void specifies that it takes no arguments, which is what you want.

You need #include <stdio.h> if you're going to call printf; this is not optional.

You don't want const in the declaration of argv. For that matter, since you don't refer to the command-line arguments, you can write int main(void).

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • there's no need to add the `void` if the empty parens are part of a definition, ie `int fun() { ... }` is perfectly fine; personally, I add the `void` for consistency... – Christoph Jul 03 '13 at 06:40
  • @Christoph: Regardless of whether it is a definition or not, empty `()` do not introduce a prototype for the function. For this reason it is always a good idea to use `(void)` even when it is a definition. It does make a difference in that regard. – AnT stands with Russia Jul 03 '13 at 08:47
  • @AndreyT: that's not the impression I got from reading the standard, but gcc agrees with you (clang at least provides a warning, but no error); the releveant standard quotes: *A function prototype is a declaration of a function that declares the types of its parameters.* (C99 6.2.1 §2), *The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.* (C99 6.7.5.3 §10), *An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters.* (§14) – Christoph Jul 03 '13 at 09:45
  • both versions (empty list in a definition or explicit `void`) equally declare a function taking no parameters, which should (in theory) make both into prototypes; in practice, one should add the `void` to make gcc happy – Christoph Jul 03 '13 at 09:47
  • @Christoph an empty list actually means an empty identifier list (see 6.7.5.3p14). And an identifier list is the C terminology for the old style non prototype function declaration / definition. You can do a check: try to call a function defined like: `void bar() {}` with an argument (e.g., `bar(42);`) and you'll see you don't get the required diagnostic you'd get with a definition like `void bar(int a) { }`. – ouah Jul 03 '13 at 13:03
  • I'm officially an idiot - the authorative section of the standard is 6.9.1 – Christoph Jul 03 '13 at 14:12
  • @Christoph: Speaking informally, "...specifies that the function has no parameters" in your quote refers to the "internal" effect of `()` on the function body. As for the "external" effect of such function definition (i.e. the *declaration* it introduces), it still follows the same rules as any other declaration: the `()` parameter list introduces no prototype. – AnT stands with Russia Jul 03 '13 at 14:28
0

C99 inline semantics are subtle - in fact, that whole part of the language (storage duration vs linkage, tentative and inline definitions) is a mess.

While inline acts as a compiler hint in definitions that include a storage class specifier (static or extern) and can basically be ignored, the semantics change if no specifier is present.

A definition like inline int fun(void) { ... } does two things:

First, it declares an identifier with external linkage, without providing a corresponding external definition. This means such a definition must be provided by a different translation unit or we end up with undefined behaviour (probably manifesting as failure to link).

Second, it provides an inline definition, which is an alternative to the external one. As the function body is visible in the current translation unit, the compiler may use it to inline the function or for type specialization.

To get the external definition, until fairly recently, I thought it necessary to repeat the function definition in another translation unit (or fake that with 4 lines of preprocessor code).

However, that's not necessary: A single line of code - a re-declaration of the function that includes the extern specifier - is enough to make the inline definition into an external one.

In your example, this would mean putting

inline int foo(void)
{
    return 42;
}

into a header file foo.h and providing a source file foo.c with contents

#include "foo.h"

// force definition to be external instead of inline
// I believe inline could be omitted, but it doesn't hurt
extern inline foo(void);

Why is this useful? As C lacks templates, generic code normally comes with a performance penalty as you need to fake generics with void* and function pointers (or, in more complex cases, vtables).

However, a sufficiently smart optimizer can get back most (potentially all) of the performance benefits of templates, but (in the absence of link-time optimization) only if the relevant function definitions are visible in the current translation unit.

While this could be achieved by putting static definitions in headers, this might increase code size to unacceptable levels in the same way that C++ templates might.

In contrast, with a C99 inline function, the compiler is free to ignore the inline definition in favour of the external one, which could even reside in a shared library.

A good example of a function that would benefit from this is qsort(), with an inline definition in stdlib.h and an external one in libc.so. There's no a priori reason for qsort() to be slower than std::sort().

Community
  • 1
  • 1
Christoph
  • 164,997
  • 36
  • 182
  • 240