我遇到了与这两篇文章(第一篇和第二篇)中描述的关于头文件中变量声明相同的问题。列出的解决方案对我来说效果很好,但我对解决方案有一个基本问题:
为什么包含警卫仍然不能解决这个问题?如果我多次包含相同的头文件,我希望包含保护将避免多次声明我的变量。
包含保护对于防止单个翻译单元中的多个修饰或类型定义很有用,即自行编译的 .c 文件及其包含的所有标头。
假设您有以下不带包含防护的标头:
啊:
struct test {
int i;
};
struct test t1;
BH:
#include "a.h"
struct test *get_struct(void);
和以下主文件:
主.c:
#include <stdio.h>
#include "a.h"
#include "b.h"
int main()
{
struct test *t = get_struct();
printf("t->i=%dn", t->i);
return 0;
}
当预处理器运行时,生成的文件将如下所示(忽略 stdio.h 的内容):
struct test {
int i;
};
struct test t1;
struct test {
int i;
};
struct test t1;
struct test *get_struct(void);
int main()
{
struct test *t = get_struct();
printf("t->i=%dn", t->i);
return 0;
}
因为 main.c 包括 a.h 和 b.h,并且因为 b.h 也包括 a.h,所以 a.h 的内容出现了两次。 这会导致struct test
定义两次,这是一个错误。 但是,变量t1
没有问题,因为每个变量都构成一个暂定定义,并且翻译单元中的多个暂定定义组合在一起以引用生成的main.o中定义的单个对象。
通过在 a.h 中添加包含防护:
#ifndef A_H
#define A_H
struct test {
int i;
};
struct test t1;
#endif
生成的预处理器输出为:
struct test {
int i;
};
struct test *get_struct(void);
int main()
{
struct test *t = get_struct();
printf("t->i=%dn", t->i);
return 0;
}
防止重复的结构定义。
但是现在让我们看看b.c,它构成了一个单独的翻译单元:
公元前:
#include "b.h"
struct test *get_struct(void)
{
return &t1;
}
预处理器运行后,我们有:
struct test {
int i;
};
struct test t1;
struct test *get_struct(void);
struct test *get_struct(void)
{
return &t1;
}
这个文件可以很好地编译,因为有一个struct test
的定义,t1
的暂定定义给了我们一个在b.o中定义的对象。
现在我们链接a.o和b.o。 链接器发现 a.o 和 b.o 都包含一个名为t1
的对象,因此链接失败,因为它被多次定义。
请注意,虽然包含保护阻止定义在单个翻译单元中多次出现,但它不会阻止它在多个翻译单元中发生。
这就是为什么t1
应该在 a.h 中有一个外部声明:
extern struct test t1;
以及一个.c 文件中的非外部声明。
切勿在头文件中定义数据或函数(静态内联除外)。始终在 .c 源文件中执行此操作。如果要使它们在其他编译单元中可见,请在头文件中将它们声明为extern
。
如果您在许多编译单元中包含相同的 .h 文件,然后链接在一起,则防护不会保护您。
包含保护您多次包含相同的包含,它将使包含中的定义仅在第一个包含点处理一次。 这意味着您在保护商标之间所做的声明不会重复,因此不会产生有关双重定义的错误。 通常不是这种情况
extern type_of_variable variable_name;
您可以多次进行,而不会受到编译器的任何抱怨......它更多地与标头中包含的类型声明或static
函数实现有关。
但是,您为什么不发布一个完整可编译代码的示例,并展示您正在尝试的内容以及为什么它不起作用。 从你的问题中,我无法猜测你假装做什么,如果某些事情在你的案例中不起作用(好吧,你引用了其他可能有答案的案例,那么你为什么不在那里使用答案呢? 那里给出的答案有什么问题?
请发布一个有效的例子来说明你担心的事情,并准确地解释为什么这些其他帖子不能解决你的问题(如果你有的话)
认为其中一个问题没有显示重复包含的实际内容,也没有显示实际的变量定义。 另一个是8年前关闭的问题,由于某种原因没有重新打开。 所以你冒着同样的风险(我确实冒着被否决的风险,这个答案实际上并没有回答你的问题,因为你实际上并没有问你发生了什么事——我不知道你的问题是否有实际问题)。