What Does It Mean for a C++ Function to Be Inline

What is the use of the `inline` keyword in C?

Note: when I talk about .c files and .h files in this answer, I assume you have laid out your code correctly, i.e. .c files only include .h files. The distinction is that a .h file may be included in multiple translation units.

static inline void f(void) {} has no practical difference with static void f(void) {}.

In ISO C, this is correct. They are identical in behaviour (assuming you don't re-declare them differently in the same TU of course!) the only practical effect may be to cause the compiler to optimize differently.

inline void f(void) {} in C doesn't work as the C++ way. How does it work in C? What actually does extern inline void f(void); do?

This is explained by this answer and also this thread.

In ISO C and C++, you can freely use inline void f(void) {} in header files -- although for different reasons!

In ISO C, it does not provide an external definition at all. In ISO C++ it does provide an external definition; however C++ has an additional rule (which C doesn't), that if there are multiple external definitions of an inline function, then the compiler sorts it out and picks one of them.

extern inline void f(void); in a .c file in ISO C is meant to be paired with the use of inline void f(void) {} in header files. It causes the external definition of the function to be emitted in that translation unit. If you don't do this then there is no external definition, and so you may get a link error (it is unspecified whether any particular call of f links to the external definition or not).

In other words, in ISO C you can manually select where the external definition goes; or suppress external definition entirely by using static inline everywhere; but in ISO C++ the compiler chooses if and where an external definition would go.

In GNU C, things are different (more on this below).

To complicate things further, GNU C++ allows you to write static inline an extern inline in C++ code... I wouldn't like to guess on what that does exactly

I never really found a use of the inline keyword in my C programs, and when I see this keyword in other people's code, it's almost always static inline

Many coders don't know what they're doing and just put together something that appears to work. Another factor here is that the code you're looking at might have been written for GNU C, not ISO C.

In GNU C, plain inline behaves differently to ISO C. It actually emits an externally visible definition, so having a .h file with a plain inline function included from two translation units causes undefined behaviour.

So if the coder wants to supply the inline optimization hint in GNU C, then static inline is required. Since static inline works in both ISO C and GNU C, it's natural that people ended up settling for that and seeing that it appeared to work without giving errors.

, in which I see no difference with just static.

The difference is just in the intent to provide a speed-over-size optimization hint to the compiler. With modern compilers this is superfluous.

What Does It Mean For a C++ Function To Be Inline?

The function is placed in the code, rather than being called, similar to using macros (conceptually).

This can improve speed (no function call), but causes code bloat (if the function is used 100 times, you now have 100 copies).

You should note this does not force the compiler to make the function inline, and it will ignore you if it thinks its a bad idea. Similarly the compiler may decide to make normal functions inline for you.

This also allows you to place the entire function in a header file, rather than implementing it in a cpp file (which you can't anyways, since then you get an unresolved external if it was declared inline, unless of course only that cpp file used it).

What does __inline__ mean ?

__inline__ is a non-standard extension. Typically, it tells the compiler: "inline this function", but being a non-standard extension we can't say with certainty unless we know which compiler this is on.

To inline is to remove the function call and place it's contents directly where the call would be made. This often removes the overhead of calling a function. It is not always optimal, because of code bloat (code getting too big and not fitting into cache), so most compilers will ignore all inline directives and do what they feel is best. This is a good thing. We humans are very poor at that kind of stuff, and it's usually considered bad practice to tell the compiler how to do its job.

Inlining is an important optimization, especially with the presence of helper functions. Imagine a function that returned the smaller of two ints:

int min(int x, int y)
{
return (x < y) ? x : y;
}

If I used this function in my code, it would be an enormous waste of time to actually make a function call, here. If I had:

int a = /* some calculation */;
int b = /* some other calculation */;

int minCalc = min(a, b);

And the compiler inlined that function, the code would become:

int a = /* some calculation */;
int b = /* some other calculation */;

int minCalc = (a < b) ? a : b;

Which removes the overhead of calling a function. From here, even more optimizations can be made as the compiler gets to work directly with the code that would have normally been hidden behind a function call. As you can see, if I have a big function and I force the compiler to inline it everywhere, the code size could grow very large very fast, and would actually hinder execution speed.

There is a standard inline keyword which was used to indicate to the compiler a function should be inlined, but nowadays most compilers don't even acknowledge it as a hint to inline the function.

There is an important side-effect of inline, though, and this can be useful. If a function is marked as inline, multiple definitions of the same function across multiple translation units is not an error. Instead, a single function definition is selected and the others are thrown out, and assumed to be the same (it's up to you to make sure this is actually okay!). This allows you to define a function within a header file without risking ODR violation errors.

Clarification over internal linkage of inline functions in C

TL;DR: GCC still defaults to its old semantics of inline, in which an inline function is still compiled as an externally visible entity. Specifying -std=c99 or -std=c11 will cause GCC to implement the standard semantics; however, the IBM compiler does not conform to the standard either. So linking will still fail, but with a different error.


Since C99, a function declaration with no declared linkage does not generate a function object. The inline definition will only be used with inline substitution, and the compiler is not obliged to perform this optimisation. It is expected that an external definition of the function exists in some other translation unit, and such a definition must exist if the function object is used, either by taking its address or by being called in a context where the compiler chooses not to perform the inline substitution.

If the inline function is declared with either static or extern, then a function object is compiled, with the indicated linkage, thereby satisfying the requirement that the function object be defined.

Prior to C99, inline was not part of the C standard, but many compilers -- particularly GCC -- implemented it as an extension. In the case of GCC, however, the semantics of inline differed slightly from the above exposition.

In C99 (and more recent), an inline function with no linkage specification is only 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." §6.7.4p7). But in the GCC extension, an inline function with no linkage specification was given external linkage (just like a non-inline function declaration). GCC then special-cased extern inline to mean "do not generate a function object", which is effectively the same as standard C99's handling of an inline function with neither extern nor static modifiers. See the GCC manual, particularly the last section.

This is only still important because GCC still defaults to using its original inline semantics unless you specify that it should conform to some C standard (using, for example, -std=c11) or disable the GNU inline semantics using -fno-gnu89-inline.

The example code, which I understand is taken from the IBM i7.1 compiler documentation, does not correctly reflect any C standard. The two definitions of foo as inline functions do not generate any actual function named foo, so the use of &foo must refer to some externally-defined foo, and there isn't one in the program. GCC will report this issue if you tell it to use C11/C99 semantics:

$ gcc -std=c99 a.c b.c
/tmp/ccUKlp5g.o: In function `g':
a.c:(.text+0xa): undefined reference to `foo'
a.c:(.text+0x13): undefined reference to `foo'
/tmp/cc2hv17O.o: In function `main':
b.c:(.text+0xa): undefined reference to `foo'
b.c:(.text+0x13): undefined reference to `foo'
collect2: error: ld returned 1 exit status

By contrast, if you ask for Gnu inline semantics, both translation units will define foo, and the linker will complain about a duplicate definition:

$ gcc -std=c99 -fgnu89-inline a.c b.c
/tmp/ccAHHqOI.o: In function `foo':
b.c:(.text+0x0): multiple definition of `foo'
/tmp/ccPyQrTO.o:a.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

Also note that GCC does not inline any function by default. You must provide some optimization option in order to enable function inlining. If you do so, and you remove the use of the address operator, you can get the program to compile:

$ cat a2.c
#include <stdio.h>
inline int foo() { return 3; }
void g() {
printf("foo called from g: return value = %d\n", foo());
}
$ cat b2.c
#include <stdio.h>
inline int foo() { return 4; }
void g();
int main() {
printf("foo called from main: return value = %d\n", foo());
g();
return 0;
}

$ # With no optimisation, an external definition is still needed:
$ gcc -std=c11 a2.c b2.c
/tmp/cccJV9J6.o: In function `g':
a2.c:(.text+0xa): undefined reference to `foo'
/tmp/cct5NcjY.o: In function `main':
b2.c:(.text+0xa): undefined reference to `foo'
collect2: error: ld returned 1 exit status

$ # With inlining enabled, the program works as (possibly) expected:
$ gcc -std=c11 -O a2.c b2.c
$ gcc -std=c11 -O1 a2.c b2.c
$ ./a.out
foo called from main: return value = 4
foo called from g: return value = 3

As indicated by the IBM documentation, the rules for C++ are distinct. This program is not valid C++ because the definitions of foo in the two translation units differ, but the compiler is not obliged to detect this error and the usual Undefined Behaviour rules apply (i.e., the standard doesn't define what will be printed). As it happens, GCC seems to show the same results as i7.1:

$ gcc -std=c++14 -x c++ a.c b.c
$ ./a.out
foo called from main: return value = 3, address = 0x55cd03df5670
foo called from g: return value = 3, address = 0x55cd03df5670

Benefits of inline functions in C++?

Inline functions are faster because you don't need to push and pop things on/off the stack like parameters and the return address; however, it does make your binary slightly larger.

Does it make a significant difference? Not noticeably enough on modern hardware for most. But it can make a difference, which is enough for some people.

Marking something inline does not give you a guarantee that it will be inline. It's just a suggestion to the compiler. Sometimes it's not possible such as when you have a virtual function, or when there is recursion involved. And sometimes the compiler just chooses not to use it.

I could see a situation like this making a detectable difference:

inline int aplusb_pow2(int a, int b) {
return (a + b)*(a + b) ;
}

for(int a = 0; a < 900000; ++a)
for(int b = 0; b < 900000; ++b)
aplusb_pow2(a, b);

Is declaring the function and then defining it inline legal in C/C++?

Yes. In fact, it's probably preferable. Source: http://www.parashift.com/c++-faq/inline-nonmember-fns.html

Rule of thumb: Declaration tells you how to call a function and use its return value. Definition says what happens when you call it. You can change a function to inline and back without modifying how you call it, so it makes sense that it's part of the definition. (Of course, what makes sense and what is true don't always match, so this is only a rule.)

Can a very short function become inlined even if it was not explicitly defined as inline?

inline is non-binding with regards to whether or not a function will be inlined by the compiler. This was originally what it was intended to do. But since then, it's been realized that whether or not a function is worth inlining depends as much on the call site as the function itself and is best left to the compiler to decide.

From https://en.cppreference.com/w/cpp/language/inline :

Since this meaning of the keyword inline is non-binding, compilers are free to use inline substitution for any function that's not marked inline, and are free to generate function calls to any function marked inline. Those optimization choices do not change the rules regarding multiple definitions and shared statics listed above.

Edit : Since you asked for C as well, from https://en.cppreference.com/w/c/language/inline :

The intent of the inline specifier is to serve as a hint for the compiler to perform optimizations, such as function inlining, which require the definition of a function to be visible at the call site. The compilers can (and usually do) ignore presence or absence of the inline specifier for the purpose of optimization.



Related Topics



Leave a reply



Submit