C - 如果将包含在 header 中的符号重复,但如果包含在源文件中则工作正常
C - Duplicate symbol if put include in header, but works fine if include in source file
我有一个 header 文件,其中包含一个 int
数组
primes.h
#ifndef P_H
#define P_H
#include <inttypes.h>
uint64_t primes[] = {
7,
11,
};
#endif
在 lib.h
文件中:
#include "primes.h"
lib.c
包括 lib.h
在 main.c
文件中:
#include "lib.h"
然后会报错:duplicate symbol '_primes' in:
但是如果我将 #include "primes.h"
移动到 lib.c
,它工作正常。
为什么将 include 放在源代码中 header 会有所不同?
您正在 h 文件中定义一个变量。这意味着您只能在一个编译单元中包含该 h 文件,即在一个 c 文件中。因此,当您将它包含在 lib.h
中并且如果 lib.h
包含在多个 c 文件中时,您就有麻烦了,即多个编译单元将定义数组变量 primes
。那不行。
规则:不要在 h 文件中定义变量。始终在 c 文件中执行此操作。
(注意:可能会有例外,但这是黄金法则 - 除非您有充分的理由将变量放入 h 文件,否则请坚持使用)
查看此答案以获取操作说明:
当您包含一个文件时,包含将替换为文件内容(这种情况一直持续到没有更多包含为止)。
- 如果
main.c
和 lib.c
都包含 lib.h
,那么它们都有单独的 lib.h
编译副本。
- 如果
lib.h
包括 primes.h
,那么它们都有自己单独的 primes.h
编译副本。
- 如果
primes.h
定义了一个数组,那么它们都有该数组的副本。
- 如果它们在两个文件中都定义了相同名称的内容,则会出现重复定义错误。
解决此问题的最佳方法是仅在源文件中定义 primes
数组及其所有值(例如 lib.c
),并且您需要使用 extern
primes.h
中的关键字没有任何值来声明您的文件之一包含实际数组(在这种情况下您可以不指定大小)。请注意,使用 extern
关键字是对编译器的一种承诺,即存在一些包含实际数组的文件,这就是为什么您仍然需要源文件中的实际数组:
// primes.h
extern uint64_t primes[];
// lib.c
#include "primes.h"
uint64_t primes[] = {
7,
11,
};
// main.c
#include "primes.h"
我有一个 header 文件,其中包含一个 int
数组primes.h
#ifndef P_H
#define P_H
#include <inttypes.h>
uint64_t primes[] = {
7,
11,
};
#endif
在 lib.h
文件中:
#include "primes.h"
lib.c
包括 lib.h
在 main.c
文件中:
#include "lib.h"
然后会报错:duplicate symbol '_primes' in:
但是如果我将 #include "primes.h"
移动到 lib.c
,它工作正常。
为什么将 include 放在源代码中 header 会有所不同?
您正在 h 文件中定义一个变量。这意味着您只能在一个编译单元中包含该 h 文件,即在一个 c 文件中。因此,当您将它包含在 lib.h
中并且如果 lib.h
包含在多个 c 文件中时,您就有麻烦了,即多个编译单元将定义数组变量 primes
。那不行。
规则:不要在 h 文件中定义变量。始终在 c 文件中执行此操作。
(注意:可能会有例外,但这是黄金法则 - 除非您有充分的理由将变量放入 h 文件,否则请坚持使用)
查看此答案以获取操作说明:
当您包含一个文件时,包含将替换为文件内容(这种情况一直持续到没有更多包含为止)。
- 如果
main.c
和lib.c
都包含lib.h
,那么它们都有单独的lib.h
编译副本。 - 如果
lib.h
包括primes.h
,那么它们都有自己单独的primes.h
编译副本。 - 如果
primes.h
定义了一个数组,那么它们都有该数组的副本。 - 如果它们在两个文件中都定义了相同名称的内容,则会出现重复定义错误。
解决此问题的最佳方法是仅在源文件中定义 primes
数组及其所有值(例如 lib.c
),并且您需要使用 extern
primes.h
中的关键字没有任何值来声明您的文件之一包含实际数组(在这种情况下您可以不指定大小)。请注意,使用 extern
关键字是对编译器的一种承诺,即存在一些包含实际数组的文件,这就是为什么您仍然需要源文件中的实际数组:
// primes.h
extern uint64_t primes[];
// lib.c
#include "primes.h"
uint64_t primes[] = {
7,
11,
};
// main.c
#include "primes.h"