内联函数的多重定义

我已经阅读了一些与此主题相关的post,但无法彻底理清我的疑问。 这可能是一个非常天真的问题。

我有一个头文件inline.h和两个翻译单元main.cpptran.cpp

代码详情如下

inline.h

 #ifndef __HEADER__ #include  extern inline int func1(void) { return 5; } static inline int func2(void) { return 6; } inline int func3(void) { return 7; } #endif 

main.c中

 #define  #include  int main(int argc, char *argv[]) { printf("%d\n",func1()); printf("%d\n",func2()); printf("%d\n",func3()); return 0; } 

tran.cpp

 //(note that the functions are not inline here) #include  int func1(void) { return 500; } int func2(void) { return 600; } int func3(void) { return 700; } 

上面的代码用g ++编译,但不用gcc编译(即使你做了与gcc相关的更改,比如将代码更改为.c,不使用任何C ++头文件等)。 显示的错误是“内联函数的重复定义 – func3”。

你能澄清为什么编译器之间存在这种差异吗?

此外,当您通过创建两个单独的编译单元( main.otran.o )并创建可执行文件a.out运行程序(g ++编译)时,获得的输出是:

500
6
700

为什么编译器会选择非内联函数的定义。 实际上,因为#include用于“添加”内联定义,我曾预期5,6,7作为输出。 我的理解是在编译期间,因为找到了内联定义,函数调用将被内联函数定义“替换”。

能否请您详细告诉我编译和链接的过程,这将导致我们产生500,6,700输出。 我只能理解输出6。

这个答案分为以下几个部分:

  1. 如何重现duplicate definition of inline function - func3duplicate definition of inline function - func3问题及其原因。
  2. 为什么定义func3是重复而不是func1
  3. 为什么用g++编译

如何产生内联函数的重复定义–func3问题

这个问题可以成功地再现

  1. tran.cpp重命名为tran.c
  2. gcc -o main main.c tran.c编译

@ K71993实际上是使用旧的gnu89内联语义进行编译,这与C99不同。tran.cpp重命名为tran.c的原因是告诉gcc驱动程序将其视为C源而不是C++源。


为什么定义func3是重复而不是func1。

GNU 89内联语义

以下文字引自GCC文档:内联函数与宏一样快解释为什么func3是重复定义而不是func1 ,因为func3 (而不是func1 )是一个外部可见的符号(在GNU89内联语义中)

当内联函数不是静态函数时,编译器必须假定可能存在来自其他源文件的调用; 由于全局符号只能在任何程序中定义一次,因此不能在其他源文件中定义该函数,因此无法集成其中的调用。 因此,非静态内联函数总是以通常的方式自行编译。

如果在函数定义中同时指定了inline和extern ,则该定义仅用于内联。 在任何情况下,函数都不会自行编译,即使您明确地引用其地址也是如此。 这样的地址成为外部引用,就好像您只声明了该函数,并且没有定义它。

C99内联语义

如果用C99标准编译,即gcc -o main main.c tran.c -std=c99 ,链接器会抱怨func1定义是重复的,因为C99中的extern inline是外部定义,如其他post和评论。

还请参考这个关于GNU89 inlineC99 inline.之间语义差异的GNU89 inline答案C99 inline.

为什么用g++编译。

使用g++编译时,源程序被视为C++源代码。 由于func1func2func3是在多个翻译单元中定义的,并且它们的定义不同,因此违反了C ++的One Defintion规则 。 由于当定义跨越多个转换单元时,编译器不需要生成恶意消息,因此行为未定义。

编译错误是因为func1()的重复定义;

因为func1()是使用extern inline定义的,所以它将产生一个外部定义。

但是,tran.c中还有一个外部定义,它会导致多个定义错误。

但是,func2()和func3()不会产生外部定义,因此不会出现重定义错误。

您可以在这里查看 http://www.greenend.org.uk/rjk/2003/03/inline.html

另外,请注意c ++和c对内联函数的处理方式不同,即使在c中,不同的标准(c89与c99)对内联函数的处理方式也不同。

也许你应该发布实际的代码。 您显示的代码段无法编译:

  • inline.h有extern inline int func1(void)这没有任何意义。
  • main.h有#define 我认为你的意思是include

一旦我修复了这些并用gcc编译,它编译得很好,我得到了以下输出

 5 6 7 

当我用g ++编译时,我得到了这个输出:

 5 6 700 

这是因为func3()在inline.h中不是静态的

从C ++的角度来看,您的代码无效,因为它公然违反了One Definition Rule。 您通过C ++编译器设置编译它的唯一原因是C ++编译器中的松散错误检查(它恰好是ODR中“无需诊断”的部分之一)。

您的代码无效C,因为它提供了函数func1重复外部定义。 请注意,从C的角度来看,它是func1 ,而不是func3 。 你的func3没有任何正式的错误。 你的func2也没问题,只要两个定义永远不会在同一个翻译单元中相互“相遇”。

您可能从编译器获取不同诊断报告的一个可能原因是您的C编译器可能以某种非标准编译器特定方式支持inline函数(C99之前的编译器或以非标准运行的现代编译器)遗产“模式”。

坦率地说,我发现很难相信你从任何编译器得到关于func3的错误报告,假设你发布的代码准确地代表了你想要编译的内容。 你发布的最有可能的不是真正的代码。

您看到的编译错误实际上是链接器错误。

gcc和g ++对static inline的处理方式略有不同。 inline是C ++的第一部分,然后在添加到标准C之前成为许多C编译器的扩展。标准语义可能不同,但它可能只是不同的实现。

它也可能与C ++代码中发生的一些疯狂的事情有关,这些代码可以消除重复的模板内容以及其他重复的东西。

基本上Inline是GCC的后期入口(我的意思是c编译器)。 “[…]内联定义不提供函数的外部定义,并且不禁止另一个翻译单元中的外部定义。内联定义提供了外部定义的替代,翻译人员可以使用它来实现任何外部定义。调用同一翻译单元中的函数。未指定对函数的调用是使用内联定义还是外部定义。“ – ISO 9899:1999(E),C99标准,第6.7.4节