多重定义和仅标头库

问题描述 投票:0回答:5

我有一个带有几个 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文件都被编译并获得自己的代码版本,然后在链接时会导致问题,但老实说我不知道如何解决这个问题。

c include multiple-definition-error
5个回答
38
投票

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

解决方案是:
您应该只定义一次出现多个定义错误的实体。
对于功能:
在头文件中声明函数原型(您将其包含在其他源文件中)并在一个且仅一个源文件中定义函数。
对于全局变量:
您在头文件(包含在其他源文件中)中声明变量 extern,然后在一个且仅一个源文件中定义该变量。


14
投票

您错过了 #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 构造,我认为它们正在工作并享受该软件。


6
投票

如果您希望将

utils.h
函数“复制”到每个使用它的地方,只需在标题中使用
static
函数即可。 (C99 中的
static inline


5
投票

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

你根本不能做这样的事情:

// header.h
void foo() { }

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

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

唯一真正的解决方案是在标头中声明

void foo();
并仅定义一次(通常在专用的
foo.c
中)。全局变量也是如此,应在标头中声明为
extern
并在源文件中定义。

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


0
投票

我最近看到一个库,它通过在其标头底部有第二个 ifdef 块来实现此目的,其中包含实现等,并要求在您的一个 c 文件中添加例如

#define FOO_IMPL
。 这意味着可以在需要的地方添加声明,但实现只会编译一次。 所以:

库.h

void foo();

#ifdef FOO_IMPL
void foo() {
  printf("foo\n");
}
#endif

(加上任何其他样板)

程序.c

...
#define FOO_IMPL // Only in exactly one compiled .c file, and nowhere else!
#include "library.h"
...

(我没有尝试编译我的示例代码,但希望它能明白要点。)

© www.soinside.com 2019 - 2024. All rights reserved.