内联函数的多重定义

时间:2010-04-27 14:58:12

标签: c inline

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

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

代码详情如下

inline.h

#ifndef __HEADER__
#include <stdio.h>
extern inline int func1(void)
{ return 5; }

static inline int func2(void)
{ return 6; }

inline int func3(void)
{ return 7; }
#endif

的main.c

#define <stdio.h>
#include <inline.h>
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 <stdio.h>
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。

6 个答案:

答案 0 :(得分:13)

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

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

    如何生成内联函数的重复定义 - func3问题

    可以成功复制问题
    1. tran.cpp重命名为tran.c
    2. 使用gcc -o main main.c tran.c
    3. 进行编译

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


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

      GNU 89内联语义

      引自GCC Document: An Inline Function is As Fast As a Macro的以下文字解释了为什么func3是重复定义而不是func1,因为func3(而不是func1)是外部可见符号(在GNU89内联语义中)

        

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

           

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

      C99内联语义

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

      请参阅this有关GNU89 inlineC99 inline.

      之间语义差异的优秀答案

      为什么使用g++进行编译。

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

答案 1 :(得分:3)

编译错误是因为func1();

的重复定义

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

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

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

您可能需要查看here http://www.greenend.org.uk/rjk/2003/03/inline.html

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

答案 2 :(得分:2)

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

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

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

5
6
7

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

5
6
700

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

答案 3 :(得分:1)

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

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

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

坦率地说,我发现很难相信你从任何编译器收到关于func3的错误报告,假设您发布的代码准确地代表了您尝试编译的内容。您发布的内容很可能是而不是真实代码。

答案 4 :(得分:0)

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

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

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

答案 5 :(得分:0)

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