正如理论所说,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,没有声明链接的函数声明不会生成函数对象。内联定义将仅与内联替换一起使用,编译器没有义务执行此优化。预期函数的外部定义存在于其他翻译单元中,如果使用函数对象,则必须存在这样的定义,无论是通过获取其地址,还是在编译器选择不执行内联替换的上下文中调用。
如果内联函数是用static
或extern
声明的,则使用所指示的链接编译函数对象,从而满足定义函数对象的要求。
在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
的使用必须引用一些外部定义的foo
0,而程序中没有。如果您告诉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
。