gpt4 book ai didi

澄清 C 中内联函数的内部链接

转载 作者:太空宇宙 更新时间:2023-11-04 01:18:16 27 4
gpt4 key购买 nike

从理论上讲,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

上面的编译错误反射(reflect)了 b.c 可以看到 a.c 中的 foo 的定义并且编译失败(这不应该是内部链接内联函数的情况)。

如果我遗漏了什么,请帮助我理解。

编辑 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 manual ,尤其是最后一节。

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

据我所知,示例代码取自 IBM i7.1 编译器文档,并未正确反射(reflect)任何 C 标准。 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

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

如 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

关于澄清 C 中内联函数的内部链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51533082/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com