是否需要 `extern` 才能使内联函数定义外部链接?

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

GCC 可以将以下 .c 文件编译并 link 为可执行文件:

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

根据 C116.7.4 函数说明符,第 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. [...]

extern关键字在上面的任何函数声明中都没有使用,那么GCC在"addsub.c"中提供add函数的外部定义是否正确?

6.2.2 Linkages of identifiers中,第5段说:

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

然而,当 extern 关键字被省略时,这似乎并不能证明将外部 linkage 添加到内联 add 函数是合理的。

不满足条件“如果翻译单元中一个函数的所有文件范围声明都包含 inline 函数说明符而没有 extern”,因为 addsub.c 包含 addsub.h,其中包含 int add(int, int);,这是不包含 inline 函数说明符的 add 的声明。

是的,GCC 运行正确。但它没有自行添加外部定义,是您指示它添加的。

关键是理解什么是 "external definition"。

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.

或多或少,外部定义是在文件范围内定义的任何内容。理解此处行为的关键是仅使用 inline 说明符声明的函数不提供外部定义。这大致意味着翻译单元不拥有这个函数的符号。因此,如果程序仅包含函数的内联定义,链接可能会失败。

为什么你的情况没有失败?这是因为 addsub.c 包括 addsub.h。翻译单元的样子是

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

顶部add的外部声明使得"inline definition"也是一个外部定义。你引用的那段话

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.

显然,并非每个函数声明都只包含 inline 说明符(在本例中为第一个声明),因此 addsub.c 中的定义实际上是一个外部定义。