GCC可以将以下.c文件编译并将其链接为可执行文件:
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
根据 C11 , 6.7.4 Functon说明符,第7段:
[...] 对于具有外部链接的函数,存在以下限制:如果使用
inline
函数说明符声明了函数,则它也应在同一翻译单元中定义。如果翻译单元中某个函数的所有文件范围声明都包含inline
而没有extern
的函数说明符,则该翻译中的定义单位是内联定义。内联定义不为函数提供外部定义,也不禁止在另一个翻译单元中使用外部定义。 [...]
上面的任何函数声明中都没有使用extern
关键字,因此GCC在“ addsub.c”中提供add
函数的外部定义时正确吗?
在 6.2.2标识符链接中,第5段说:
如果函数标识符的声明中没有存储类说明符,则其链接的确定与使用存储类说明符
extern
声明时完全相同。 [...]
但是,当省略add
关键字时,似乎没有理由向内联extern
函数添加外部链接。
答案 0 :(得分:2)
是的,GCC运行正常。但是,您没有指示它添加任何自定义的外部定义。
关键是要了解什么是“外部定义”。
6.9外部定义
4如5.1.1.1所述,程序文本后面的单位 预处理是翻译单元,由一系列序列组成 外部声明。这些被称为“外部”,因为 它们出现在任何功能之外(因此具有文件作用域)。如 在6.7中讨论过,该声明也导致存储 保留给由标识符命名的对象或函数的对象是 定义。
5外部定义是外部声明,也是 函数的定义(内联定义除外)或 目的。如果在外部链接中使用通过外部链接声明的标识符 表达式(不是作为sizeof或_Alignof的操作数的一部分 运算符,其结果是一个整数常量),在整个位置上 程序中应该只有一个外部定义 标识符否则,最多只能有一个。
或多或少,外部定义是在文件范围内定义的任何内容。理解此行为的关键在于仅用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
的外部声明使“内联定义”也成为外部定义。在您引用的段落中
如果翻译中函数的所有文件范围声明 单位包括没有extern的内联函数说明符,则 该翻译单元中的定义是内联定义。
很明显,并非每个函数声明都只包含inline
说明符(在这种情况下为第一个声明),因此addsub.c
中的定义实际上是一个外部定义。
答案 1 :(得分:1)
不满足条件“如果翻译单元中某个函数的所有文件范围声明都包含inline
函数说明符而不包含extern
”,因为addsub.c包含addsub.h,其中包含int add(int, int);
,这是add
的声明,其中不包含inline
函数说明符。