C中内联函数内部链接的说明



正如理论所说,inline函数在C中具有内部/静态链接,也就是说,它们只在单个翻译单元中可见。因此,在两个单独的文件中定义的内联函数不应该能够看到彼此,并且两者都有自己的地址空间。

我正在尝试使用以下文件

***cat a.c*** 
#include <stdio.h>
inline int foo()
{
return 3;
}
void g()
{
printf("foo called from g: return value = %d, address = %#pn", 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 = %#pn", 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

上述编译错误反映出b.c能够在

a.c请帮我理解我是否遗漏了什么。

编辑1:正在尝试这种联系的理论。https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_71/rzarg/inline_linkage.htm

TL;DR:GCC仍然默认为inline的旧语义,其中inline函数仍然编译为外部可见实体。指定-std=c99-std=c11将导致GCC实现标准语义;然而,IBM编译器也不符合该标准。因此,链接仍然会失败,但会出现不同的错误。


由于C99,没有声明链接的函数声明不会生成函数对象。内联定义将仅与内联替换一起使用,编译器没有义务执行此优化。预期函数的外部定义存在于其他翻译单元中,如果使用函数对象,则必须存在这样的定义,无论是通过获取其地址,还是在编译器选择不执行内联替换的上下文中调用。

如果内联函数是用staticextern声明的,则使用所指示的链接编译函数对象,从而满足定义函数对象的要求。

在C99之前,inline不是C标准的一部分,但许多编译器——尤其是GCC——将其作为扩展实现。然而,在GCC的情况下,inline的语义与上述说明略有不同。

在C99(以及最近的版本(中,没有链接规范的内联函数只是内联定义("内联定义不为函数提供外部定义,也不禁止在另一个翻译单元中进行外部定义。"§6.7.4p7(。但在GCC扩展中,没有链接规范的内联函数被赋予了外部链接(就像非内联函数声明一样(。GCC将extern inline的大小写特殊化,表示"不生成函数对象",这实际上与标准C99对既不使用extern也不使用static修饰符的内联函数的处理相同。请参阅GCC手册,特别是最后一节。

这仍然很重要,因为GCC仍然默认使用其原始的inline语义,除非您指定它应该符合某些C标准(例如使用-std=c11(或使用-fno-gnu89-inline禁用GNU内联语义。

据我所知,示例代码取自IBMi7.1编译器文档,并不能正确反映任何C标准。foo作为内联函数的两个定义并没有生成任何名为foo的实际函数,因此&foo的使用必须引用一些外部定义的foo0,而程序中没有。如果您告诉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

相比之下,如果您要求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

还要注意,GCC默认情况下不内联任何函数。必须提供一些优化选项才能启用函数内联。如果你这样做,并且你删除了地址运算符的使用,你可以得到程序来编译:

$ cat a2.c
#include <stdio.h>
inline int foo() { return 3; }
void g() {
printf("foo called from g: return value = %dn", foo());
}
$ cat b2.c
#include <stdio.h>
inline int foo() { return 4; }
void g();
int main() {
printf("foo called from main: return value = %dn", 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

正如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

内联是一种优化,编译器可以自行决定是否执行(如果可能的话(。因此,不能保证符号不会导出到生成的对象文件中。为此,请使用static

相关内容

  • 没有找到相关文章

最新更新