Is `extern` required to make an inline function definition externally linked?
Asked Answered
T

2

2

GCC can compile and link the following .c files into an executable:

main.c

#include <stdio.h>
#include "addsub.h"

int main(void)
{
    printf("%d %d\n", add(1, 2), sub(1, 2));
    return 0;
}

addsub.c

#include "addsub.h"

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

int sub(int a, int b)
{
    return add(a, -b);
}

addsub.h

#ifndef ADDSUB_H__
#define ADDSUB_H__

int add(int, int);
int sub(int, int);

#endif

According to C11, 6.7.4 Functon specifiers, paragraph 7:

[...] 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. [...]

The extern keyword has not been used in any of the function declarations above, so is GCC correct in providing an external definition of the add function in "addsub.c"?

In 6.2.2 Linkages of identifiers, paragraph 5 says:

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. [...]

However, that does not seem to justify adding external linkage to the inline add function when the extern keyword is omitted.

Trabue answered 15/6, 2020 at 16:41 Comment(6)
@EricPostpischil That seems reasonable. Do you want to post it as an answer (if you think it useful) or do you think I should just delete the question?Trabue
place the 'inline' modifiers in the header file, not on the actual function body (suggest this modifier be applied to both functions, not just oneConstituency
however, the 'inlined' function needs to be in the header file, NOT in some other file. A function in some other file *.c can never be 'inline'd in a different *.c fileConstituency
@Constituency I'm already aware of that. My point of confusion was where the external definition of the function was coming from.Trabue
Related: What's the difference between static inline, extern inline and a normal inline function? - example of how it compiles with or without (link error) an extern inline declaration to instantiate a non-inline version of a function.Interconnect
Does this answer your question? What's the difference between static inline, extern inline and a normal inline function?Lampe
R
2

The condition “If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern” is not satisfied because addsub.c includes addsub.h, which contains int add(int, int);, which is a declaration of add that does not include the inline function specifier.

Reorientation answered 15/6, 2020 at 17:4 Comment(0)
C
3

Yes, GCC is acting correctly. But it did not add an external definition of its own accord, you instructed it to.

The key is understanding what an "external definition" is.

6.9 External definitions

4 As discussed in 5.1.1.1, the unit of program text after preprocessing is a translation unit, which consists of a sequence of external declarations. These are described as ''external'' because they appear outside any function (and hence have file scope). As discussed in 6.7, a declaration that also causes storage to be reserved for an object or a function named by the identifier is a definition.

5 An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.

More or less, an external definition is anything that gets defined at file scope. The key to understanding the behavior here is that a function declared with just the inline specifier does not provide an external definition. This roughly means that a translation unit doesn't own the symbol for this function. So if a program only contains inline definitions of a function, linkage may fail.

Why does it not fail in your case? It's because addsub.c includes addsub.h. What that translation unit looks like is

int add(int, int);
int sub(int, int);

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

int sub(int a, int b)
{
    return add(a, -b);
}

The external declaration of add at the top makes the "inline definition" be an external definition too. By the very paragraph you quote

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.

Clearly, not every declaration of the function contains just the inline specifier (the first declaration in this case), so the definition in addsub.c is in fact an external definition.

Composition answered 15/6, 2020 at 17:5 Comment(2)
Thanks for your great answer. I accepted Eric's because he got there first with a very concise answer. My point of confusion was indeed a misreading of 6.7.4p7.Trabue
@IanAbbott - No need to explain. I'm glad you got your confusion sorted. I opted to post my answer next to Eric's in the hopes it will help others who are less versed in the standard than you.Composition
R
2

The condition “If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern” is not satisfied because addsub.c includes addsub.h, which contains int add(int, int);, which is a declaration of add that does not include the inline function specifier.

Reorientation answered 15/6, 2020 at 17:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.