【问题标题】:Multiple definition and header-only libraries多个定义和仅标头库
【发布时间】:2012-01-02 08:52:02
【问题描述】:

我有一个包含多个 c 和 h 文件的 C 程序。我决定将程序的一部分设为“仅标题”,因此我将代码从 c 移至 h。现在我遇到了多重定义问题,我不知道为什么。例如:

main.c includes utils.h
vector.c includes utils.h

我将 utils.c 中的所有内容都移到了 utils.h(当然还从项目中删除了 utils.c)。 utils.h 以

开头
#ifndef UTILS_H_
#define UTILS_H_

// and end with:
#endif

为了确保我的后卫是独一无二的,我尝试更改它(例如:UTILS718171_H_)但它不起作用。

编译器仍然抱怨:

/tmp/ccOE6i1l.o: In function `compare_int':
ivector.c:(.text+0x0): multiple definition of `compare_int'
/tmp/ccwjCVGi.o:main.c:(.text+0x660): first defined here
/tmp/ccOE6i1l.o: In function `compare_int2':
ivector.c:(.text+0x20): multiple definition of `compare_int2'
/tmp/ccwjCVGi.o:main.c:(.text+0x6e0): first defined here
/tmp/ccOE6i1l.o: In function `matrix_alloc':
ivector.c:(.text+0x40): multiple definition of `matrix_alloc'
/tmp/ccwjCVGi.o:main.c:(.text+0x0): first defined here
...

问题可能是这样的:所有的 c 文件都被编译并获得他们自己的代码版本,然后在链接时它会导致问题,但老实说我不知道​​如何解决这个问题。

【问题讨论】:

  • 尝试发布 utils.h。请记住只将原型或常量放在 utils.h 中
  • question 2174657 提供了一些关于何时/如何使用仅标头库的信息。

标签: c include multiple-definition-error


【解决方案1】:

如果您在头文件中定义变量并将头文件包含在多个 c 文件中,您一定会遇到多重定义错误,因为您违反了一个定义规则(ODR),该规则指出一个翻译单元(头文件+源文件)中应该只有一个定义。

解决办法是:
您应该只定义一次获得多个定义错误的实体。
对于函数:
在头文件中声明函数原型(包括在其他源文件中)并在唯一一个源文件中定义函数。
对于全局变量: 您在头文件中声明变量 extern(您包含在其他源文件中),然后在 one and only one 源文件中定义变量。

【讨论】:

  • 为什么我可以在类头中定义成员函数?我知道这意味着函数将被内联,但这是唯一的原因吗?
  • 虽然这个答案被接受了,但这不是这个问题的正确答案,因为它错过了整个要点:OP希望使用一个仅包含标题的文件,所有声明和定义都在同一个文件中.即使没有 Arduino IDE,在 Arduino 环境中也有必要这样做是有正当理由的。该答案仅表明您不应使用仅标头文件。有三种方法可以解决多重定义问题,每种方法都有不同的含义和成本。 IMO 此处适用的解决方案是将任何非成员函数声明为内联。
  • 为什么ifndef 不能避免变量的多重定义?
  • @rsonx 因为标头被包含一次,但被多个源文件包含,每个源文件都构建自己的目标文件。每个都只包含一次标头,但目标文件不止一个。多个定义只有在尝试将它们链接在一起时才会显现出来。
【解决方案2】:

您错过了 #ifndef _FOO_H / #define _FOO_H / #endif 构造的要点。这只能防止在一个文件中包含多个内容。例如,它们可以防止这种情况:

foo.h:

  #ifndef _FOO_H 
  #define _FOO_H

  /* some C stuff here */

  #endif /* _FOO_H */

foo.c:

   #include <foo.h>
   #include <bar.h>
   ...

bar.h:

   #include <foo.h>
   ...

注意 foo.c 和 bar.h 都包含 foo.h;这里 #ifdef _FOO_H / #define _FOO_H / #endif 防止 foo.c 中的双重包含(bar.h 中包含的 foo.h 不会重新定义东西)

现在是下一部分。

为什么要将函数实现从 utils.c 移到 utils.h?另外,您为什么决定将其设为“仅标题”?

您可以这样做,具体取决于您的编译器是否支持static inline 函数;但即便如此,也不建议这样做,因为它很可能会使您的程序不必要地臃肿,因为您的 util 函数很可能非常复杂并且无论如何都不能内联。

因此,将其改回您之前拥有的 utils.c 和 utils.h 结构,我想,它正在工作并享受该软件。

【讨论】:

  • @Jeff Kelley:感谢您的编辑 :) 在不戴眼镜的情况下在黑暗的终端上写东西很难。
【解决方案3】:

您违反了单一定义规则。每个函数都必须精确定义一次,而您最终在每个翻译单元中定义它

你只是不能做这样的事情:

// header.h
void foo() { }

// file1.c
#include "header.h"

// file2.c
#include "header.h"

唯一真正的解决方案是在标题中声明 void foo();定义一次(通常在专用的foo.c 中)。全局变量也是如此,应该在头文件中声明为extern,并在源文件中定义。

包括守卫与此无关。它们仅用于防止递归自包含或冗余多重包含在一个翻译单元中

【讨论】:

    【解决方案4】:

    如果您打算将utils.h 函数“复制”到每个使用它的地方,只需在标题中使用static 函数。 (static inline 在 C99 中)

    【讨论】:

      猜你喜欢
      • 2011-04-27
      • 1970-01-01
      • 2012-09-18
      • 1970-01-01
      • 2015-05-09
      • 1970-01-01
      • 2015-01-20
      • 1970-01-01
      • 2021-05-05
      相关资源
      最近更新 更多