Stas Jaro
Stas Jaro

Reputation: 4885

Why is static keyword required for inline function?

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());
}

Upvotes: 3

Views: 2106

Answers (2)

Christoph
Christoph

Reputation: 169603

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().

Upvotes: 0

Keith Thompson
Keith Thompson

Reputation: 263307

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).

Upvotes: 3

Related Questions