Inline Functions in C
The inline functions introduced in C99 allow reducing call overhead, favoring better performance compared to the traditional approach based on normal functions or parameterized macros.
However, the inline keyword is only a suggestion to the compiler, which can decide whether to actually insert the function body at the point where it is called.
In this article we will see how to declare inline functions, the related linkage implications and the restrictions imposed by the standard.
inline Functions
Function declarations in C99 have an additional option that does not exist in C89: they can contain the inline keyword.
To understand the effect of inline, we must imagine the machine-level instructions generated by the C compiler to handle the call and return from a function.
At the machine level, several instructions may be necessary to prepare the call; the call itself involves jumping to the first instruction of the function and there may be additional instructions executed by the function at the start of execution.
If the function has arguments, these must be copied (since in C they are passed by value).
Returning from a function requires similar effort, both from the called function and from the calling function.
The set of operations necessary to call and return from a function is often defined as call overhead, because it is extra work compared to what the function should actually do.
Call Overhead
Call overhead is the additional work that the program must do to call a function and return from it. This work includes preparing parameters, jumping to the function and returning to the calling function.
Although the overhead of a single call may slow down the program by a minimal amount, in some cases it can have a significant impact (for example, when a function is called millions or billions of times, or on very slow processors, or in embedded systems with strict timing constraints).
In the C89 standard, the only way to avoid the overhead of a function call was to use a parameterized macro. This is because macros are expanded directly at the point where they are called, without any call overhead. It is essentially a textual substitution.
However, parameterized macros have several disadvantages:
- They do not check argument types;
- They can be difficult to write and read;
- Recursion cannot be exploited;
- They cannot call other macros.
C99 offers a better solution to the problem: creating an inline function. The inline keyword suggests an implementation in which the compiler replaces the function call with the machine instructions that represent its body. This eliminates the call overhead, although the compiled code may increase in size.
Declaring a function as inline does not actually force the compiler to apply the process to the function, but is a simple suggestion for the compiler to try to make calls to that function as fast as possible, possibly expanding the function inline where it is called.
The compiler is free to ignore this suggestion. In this sense, inline is similar to the register and restrict keywords, which the compiler can use to optimize program performance, but can also choose to ignore.
In summary:
Inline Function
An inline function is a function declared with the inline keyword. This suggests to the compiler to replace calls to the function with the function body itself, eliminating call overhead.
Inline Definitions
An inline function has the inline keyword as part of its declaration specifiers:
inline double Average(double FirstValue, double SecondValue)
{
return (FirstValue + SecondValue) / 2;
}
Here the situation becomes more complex. The Average function has external linkage, so other source files might contain functions called Average. However, the definition of Average is not considered an external definition by the compiler (but rather an inline definition), so attempting to call Average from another file might cause an error.
There are two ways to avoid this problem:
-
Add the
statickeyword to the function definition:static inline double Average(double FirstValue, double SecondValue) { return (FirstValue + SecondValue) / 2; }Now
Averagehas internal linkage, and cannot be called from other files. Other files can contain their own definitions ofAverage, which can differ from this one. -
Provide an external definition for
Average, so that it can be called from other files. One way to do this is to write theAveragefunction a second time (without usinginline) and put it in a different source file. However, having two versions of the same function can be risky, because there is no guarantee that they will remain consistent if the program is modified.
A better approach is as follows:
-
We put the inline definition of
Averagein a header file (for exampleAverage.h):#ifndef AVERAGE_H #define AVERAGE_H inline double Average(double FirstValue, double SecondValue) { return (FirstValue + SecondValue) / 2; } #endif -
We then create a paired source file,
Average.c:#include "Average.h" extern double Average(double FirstValue, double SecondValue);
Any file that needs to call the Average function can simply include Average.h, which contains the inline definition of Average. The Average.c file contains a prototype for Average that uses the extern keyword, which causes the function definition, included from Average.h, to be treated as an external definition in Average.c.
The general rule in C99 is that if all file-level declarations of a function in a given file include inline but not extern, the function definition in that file is inline.
If the function is used elsewhere in the program (including the file that contains the inline definition), an external definition of the function must be provided by another file.
When the function is called, the compiler can choose to make an ordinary call (using the external definition) or an inline expansion (using the inline definition).
There is no way to know which choice the compiler will make, so it is essential that the two definitions are consistent. The technique just illustrated (using Average.h and Average.c) ensures that these definitions are the same.
Restrictions on Inline Functions
Since inline functions are implemented in a rather different way from ordinary functions, they are subject to special rules and limitations. Variables with static storage duration represent a particular problem for inline functions with external linkage. Consequently, C99 imposes the following restrictions on an inline function with external linkage (but not on one with internal linkage):
- The function cannot define a modifiable
staticvariable. - The function cannot contain references to variables with internal linkage.
However, such a function can define a variable that is both static and const; each inline definition of the function can then create its own copy of that variable.
Using Inline Functions with GCC
Some compilers, including GCC, supported inline functions before the C99 standard. Consequently, the rules on using inline functions may vary depending on the version. In particular, the scheme described above (using the Average.h and Average.c files) might not work with some compilers.
Functions declared both static and inline should work correctly, regardless of the GCC version. This strategy is legal in C99, so it is the safest. A static inline function can be used in a single file or included in a header file and inserted into any source file that needs it.
There is another way to share an inline function among multiple files, compatible with older versions of GCC but in conflict with C99. This technique involves putting the function definition in a header file, specifying that the function is both extern and inline, and then inserting a second copy of the definition (without the extern and inline words) in another source file. This way, if the compiler chooses to apply the inline process to the function for any reason, it will still have a definition available.
One final note on GCC: functions are made inline only when optimization is requested via the -O option on the command line.
In Summary
In this lesson we have learned new concepts regarding functions:
- Function overhead: calling and returning from a function involves additional costs (overhead), which inline functions aim to reduce.
inlinekeyword: indicates that the compiler can replace the call with the function body, eliminating the execution jump.- External vs. internal linkage: if the inline function has external linkage, it is necessary to also provide an "external" definition to allow calling from other files.
- Restrictions on inline functions: some static variables or those with internal linkage are not compatible with inline functions that have external linkage.
- GCC support: different versions of GCC may handle inlines differently than the C99 standard; the safest strategy remains using
static inlineor following the "header + external definition" scheme.