Rnd*_*p13 2 c inline-functions linkage
从理论上讲,inline函数在 C 中具有内部/静态链接,也就是说,它们仅在单个翻译单元中可见。因此,在两个单独的文件中定义的内联函数不应该能够看到彼此,并且两者都有自己的地址空间。
我正在尝试使用以下文件
***cat a.c***
#include <stdio.h>
inline int foo()
{
return 3;
}
void g()
{
printf("foo called from g: return value = %d, address = %#p\n", foo(), &foo);
}
***cat b.c***
#include <stdio.h>
inline int foo()
{
return 4;
}
void g();
int main()
{
printf("foo called from main: return value = %d, address = %#p\n", foo(), &foo);
g();
return 0;
}
gcc -c a.c
gcc -c b.c
gcc -o a.out a.o b.o
b.o: In function `foo':
b.c:(.text+0x0): multiple definition of `foo'
a.o:a.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
上面的编译错误反映了bc能够看到acfoo中的定义并且编译失败(内部链接的内联函数不应该出现这种情况)。
如果我遗漏了什么,请帮助我理解。
编辑1:正在尝试这个链接的理论。 https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_71/rzarg/inline_linkage.htm
TL;DR: GCC 仍然默认其旧语义inline,其中inline函数仍然被编译为外部可见实体。指定-std=c99or-std=c11将导致 GCC 实现标准语义;然而,IBM 编译器也不符合该标准。因此链接仍然会失败,但会出现不同的错误。
从 C99 开始,没有声明链接的函数声明不会生成函数对象。内联定义仅与内联替换一起使用,编译器没有义务执行此优化。预计函数的外部定义存在于其他翻译单元中,并且如果使用函数对象,则必须存在这样的定义,无论是通过获取其地址还是在编译器选择不执行的上下文中调用内联替换。
如果使用static或声明内联函数extern,则使用指示的链接编译函数对象,从而满足定义函数对象的要求。
在 C99 之前,inline它不是 C 标准的一部分,但许多编译器(尤其是 GCC)将其作为扩展实现。然而,就 GCC 而言, 的语义inline与上述说明略有不同。
在 C99(及更新版本)中,没有链接规范的内联函数只是内联定义(“内联定义不提供函数的外部定义,并且不禁止另一个翻译单元中的外部定义。”§6.7 .4p7)。但在 GCC 扩展中,没有链接规范的内联函数被赋予了外部链接(就像非内联函数声明一样)。然后,GCC 进行特殊处理,表示“不生成函数对象”,这实际上与标准 C99 对既不带修饰符也不带修饰符extern inline的内联函数的处理相同。请参阅GCC 手册,特别是最后一节。externstatic
这仍然很重要,因为 GCC 仍然默认使用其原始inline语义,除非您指定它应该符合某些 C 标准(例如,使用-std=c11)或使用 禁用 GNU 内联语义-fno-gnu89-inline。
据我所知,示例代码取自 IBM i7.1 编译器文档,它没有正确反映任何 C 标准。as 内联函数的两个定义foo不会生成任何名为 的实际函数foo,因此使用时&foo必须引用一些外部定义的foo,而程序中并没有。如果您告诉 GCC 使用 C11/C99 语义,GCC 将报告此问题:
$ 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
Run Code Online (Sandbox Code Playgroud)
相比之下,如果您要求 Gnu 内联语义,两个翻译单元都会定义foo,并且链接器会抱怨重复的定义:
$ 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
Run Code Online (Sandbox Code Playgroud)
另请注意,GCC 默认情况下不内联任何函数。您必须提供一些优化选项才能启用函数内联。如果这样做,并且删除了地址运算符的使用,则可以编译程序:
$ 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
Run Code Online (Sandbox Code Playgroud)
正如 IBM 文档所示,C++ 的规则是不同的。该程序不是有效的 C++,因为foo两个翻译单元中 的定义不同,但编译器没有义务检测此错误,并且适用通常的未定义行为规则(即,标准未定义将打印的内容)。碰巧,GCC 似乎显示了与 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
Run Code Online (Sandbox Code Playgroud)