【问题标题】:multiple definition of inline function内联函数的多重定义
【发布时间】:2010-04-27 14:58:11
【问题描述】:

我浏览了一些与此主题相关的帖子,但无法完全解决我的疑问。这可能是一个非常幼稚的问题。

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

具体代码如下

内联.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)运行程序(g++ 编译)并创建可执行文件a.out 时,获得的输出为:

500
6
700

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

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

【问题讨论】:

  • 你不是在“#ifndef HEADER”之后缺少一个“#define HEADER”吗??
  • 您将哪些命令行开关传递给 GCC? C99 中inline 的规范与传统的GCC 特定扩展不同。您尝试使用哪种规范?
  • 1) #ifndef __HEADER__(预处理器)以一个或多个下划线开头的标识符保留用于语言实现。 2) 哪种语言,C 还是 C++?
  • 您的示例缺少#define __HEADER__。另外,这是一个链接(我认为它可能会有所帮助):greenend.org.uk/rjk/tech/inline.html

标签: c inline


【解决方案1】:

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

  1. 如何重现duplicate 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 驱动程序将其视为 @ 987654333@ 源而不是 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 是前面提到的外部定义在其他帖子和 cmets 中。

关于GNU89 inlineC99 inline.之间的语义差异,另请参考this的优秀答案

为什么使用 g++ 编译。

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

【讨论】:

    【解决方案2】:

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

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

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

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

    你可能想看看herehttp://www.greenend.org.uk/rjk/2003/03/inline.html

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

    【讨论】:

    • 据我所知纯 C89 不支持 inline 关键字,只有一些扩展支持内联,但在这种情况下,您不再用纯 C89 而是在某些方言中编写C89 的。
    【解决方案3】:

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

    • inline.h 有 extern inline int func1(void) 这没有任何意义。
    • main.h 有 #define &lt;stdio.h&gt; 我想你的意思是 include

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

    5
    6
    7
    

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

    5
    6
    700
    

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

    【讨论】:

    • extern inline 被一些编译器用来表示“如果您看到使用并且可以内联,则将其内联,但如果您看到无法内联的使用,则假定另一个编译单元将提供非-此函数的内联版本”。无法内联可能是因为函数的地址已分配给变量或传递给另一个函数。我相信 gcc 头文件经常使用extern inline
    • @nategoose: extern inline 是 C99 语言的标准特性。这与“某些编译器”无关。所有符合 C99 的编译器都必须将 extern inline 定义识别为函数的外部定义。
    • @AndreyT:一些编译器是 C99。 :-) 一些不是(或不在 C99 模式下)的仍然接受extern inline
    【解决方案4】:

    从 C++ 的角度来看,您的代码是无效的,因为它公然违反了单一定义规则。您设法通过 C++ 编译器对其进行编译的唯一原因是 C++ 编译器中的松散错误检查(它恰好是 ODR 中“不需要诊断”的部分之一)。

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

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

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

    【讨论】:

      【解决方案5】:

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

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

      这也可能与 C++ 代码中发生的一些疯狂的事情有关,这些代码摆脱了重复的模板内容,也捕获了其他重复的内容。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 2011-01-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-14
          • 2011-02-24
          相关资源
          最近更新 更多