8

To place an inline function definition in a C header file for a function that should be inlined into multiple other units, should inline or static inline be used? I've been Googling for a little while but there seems to be no concise explanation of the difference so far.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
Toby
  • 9,696
  • 16
  • 68
  • 132
  • 2
    See also http://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99?rq=1 – Barmar May 16 '14 at 16:06

1 Answers1

21

The proper way to inline a function in C is as follows:

  • Place an inline function in the header
  • Create an implementation file that includes that header
  • Place an extern inline function in the implementation file.

example.h

inline int example(int val) {
    return (val << 2) | 1;
}

example.c

#include "example.h"

extern inline int example(int val);

Can't you just declare a static inline in the header, without .c?

This would result in separate independent function definitions in each translation unit from which the header is included. In addition in increasing the size of compiled code unnecessarily, this would produce some unexpected behavior when you obtain a pointer to your inline functions: rather than producing the same address, the addresses of the inline function taken in different translation units would produce different values.

but if one guards the header file the re-defining can be avoided, can't it?

No, absolutely not. This has nothing to do with multiple inclusions of the same header. Each translation unit is compiled separately from other translation units, so when the compiler sees a static function, it has no choice but to create a private duplicate invisible from the outside of the translation unit.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 2
    Can't you just declare a static inline... in the header, without .c? – this May 16 '14 at 16:03
  • 1
    @self. Because that would result in re-defining the function in each compilation unit where the header is included, so comparing function pointers for theoretically the same function would produce unexpected results. – Sergey Kalinichenko May 16 '14 at 16:04
  • 1
    @dasblinkenlight but if one guards the header file the re-defining can be avoided, can't it? – Peter Varo May 16 '14 at 16:06
  • 3
    @PeterVaro No. It has nothing to do with multiple inclusions of the same header - these are absolutely fine. Each translation unit is compiled separately from other translation units, so when the compiler sees a static function, it has no choice but to create a private duplicate invisible from the outside of the translation unit. – Sergey Kalinichenko May 16 '14 at 16:08
  • 1
    make sense, but what about `static inline` then? – Peter Varo May 16 '14 at 16:12
  • 1
    @PeterVaro That is what happens with `static inline`. With non-static `inline` you end up with linker errors in debug builds, when inlining is disabled (I was fighting this very problem two weeks ago). – Sergey Kalinichenko May 16 '14 at 16:14
  • 1
    that also make sense, thanks:) however in your example above, you use `inline` without `static`.. – Peter Varo May 16 '14 at 16:16
  • 1
    @PeterVaro That's right - if you place `inline`s in a header, they should be without `static`, and they also should have a corresponding `extern` in the implementation. You use `static inline` when you define a private inline function that needs to be visible inside a single .C file. There is no reason for `static inline` to appear in a header file (although I've seen this happen more than once; I assume this was done to fix the build). – Sergey Kalinichenko May 16 '14 at 16:21
  • 1
    I don't think that you should worry about extra copies of the function, because a) if it is inlined you will end up with an extra copy anyway. b) if they are all inlined then the private copy could be stripped later. – Grady Player May 16 '14 at 16:21
  • @GradyPlayer The problem happens not when the functions are inlined, but when they are not inlined, i.e. in debug builds. In this situation, private copies would not get stripped from object files that use the function. – Sergey Kalinichenko May 16 '14 at 16:24
  • I guess you could imagine a scenario that it would matter in... like if you were doing some massive meta-programming and trying to fit it onto very limited hardware... but I think those types of scenarios are exceptions to the rules... be aware of them sure, but generally it isn't going to matter in my workflow. – Grady Player May 16 '14 at 16:27
  • 1
    +1, but I don't think that `static inline` is as useless as it may appear, here. For small "pure" functions, this is a valid strategy, for cases where you don't need function pointers and the function is inlined anyhow, since it is small enough. `static` functions that are not used are optimized away by modern compilers. – Jens Gustedt May 16 '14 at 20:36
  • @dasblinkenlight Well explained, muchos gracias! To be clear did you mean that the extern declaration should be placed in any .c where the function is to be inlined, or just in the .c that matches the .h? – Toby May 17 '14 at 10:55
  • 2
    @Toby Only one `extern inline` is needed - in the .c file that matches the .h file. – Sergey Kalinichenko May 17 '14 at 11:00
  • Is `extern` actually needed? Aren't function declarations `extern` by default? – Drew McGowen Aug 30 '14 at 14:41
  • 2
    @DrewMcGowen Yes, `extern` is the default qualifier. However, since `inline` is also a qualifier, the `extern` becomes necessary again, because the default rules no longer apply. – Sergey Kalinichenko Aug 30 '14 at 16:13
  • How this scenario could happen - "the addresses of the inline function taken in different translation units would produce different values". How can comparing these addresses be possible anyway, if they are in different units? – meguli Jan 29 '17 at 12:04
  • @meguli For example, each translation unit could print the address of the function, and you would compare the printouts. They could also pass addresses of the function to another function, that would do the comparison. – Sergey Kalinichenko Jan 29 '17 at 12:10
  • @dasblinkenlight Wouldn't passing the address to a function work the way we intended? After all, even though the addresses are different the actual code that will be run in the end are copies of each other? By the way, will those be inlined too, if we call the function at some other place through its pointer? – meguli Jan 29 '17 at 12:53