C:外部变量声明并包含守卫[关闭]

Posted

技术标签:

【中文标题】C:外部变量声明并包含守卫[关闭]【英文标题】:C: Extern variable declaration and include guards [closed] 【发布时间】:2021-05-25 18:02:29 【问题描述】:

关于头文件中的变量声明,我遇到了与这两篇文章(First 和Second)中描述的相同的问题。列出的解决方案对我来说效果很好,但我对解决方案有一个基本问题:

为什么包含保护仍然不能解决这个问题?如果我多次包含同一个头文件,我希望包含保护可以避免我的变量被多次声明。

【问题讨论】:

假装你是预处理器。现在尝试自己用您的解决方案“预处理”一个玩具示例,看看会出现哪些问题。 include 保护避免在一个编译中包含多个。当您将单独的编译链接在一起时,您会得到重复的变量。 请edit您的问题并显示minimal reproducible example。链接问题的答案只能从错误消息中猜测您可能在头文件中有变量的定义(而不是声明)。不要描述部分代码,而是显示代码。复制并粘贴最小示例文件 a.ca.hb.cb.h 的内容、错误消息和警告,以及用于编译和链接代码的命令(如果可能)。 @Bodo 我没有特别的问题。正如我所说,我能够用链接的问题来解决它。我只是不太明白为什么包含警卫不能防止多重定义变量出现这种错误。我的问题只是为了澄清解决方案。 @drdolphin 您的问题不够清晰是因为您忽略了有关“此类错误”或“此问题”的详细信息。您写的是“要声明多次的变量”,而我猜您指的是如果变量被多次定义会出现的问题。添加示例代码将使这一点变得清晰,从而提高问题的质量。 【参考方案1】:

包含保护对于防止单个翻译单元中的多个声明或类型定义很有用,即一个 .c 文件,它与它所包含的所有头一起编译。

假设您有以下没有包含保护的标头:

啊哈:

struct test 
    int i;
;

struct test t1;

b.h:

#include "a.h"

struct test *get_struct(void);

还有以下主文件:

main.c:

#include <stdio.h>
#include "a.h"
#include "b.h"

int main()

    struct test *t = get_struct();
    printf("t->i=%d\n", 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=%d\n", 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=%d\n", t->i);
    return 0;

防止重复的结构定义。

但是现在让我们看看 b.c 构成一个单独的翻译单元:

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 文件中的非外部声明。

【讨论】:

第一个例子很糟糕,因为它违反了最重要的规则:Do not define data or functions (except static inline) in the header files. @0___________ 这正是重点。它说明了为什么标头保护不能防止不同文件中的多个定义。 感谢您写得很好的回答!这也解释了为什么它首先是编译错误,然后在我实现包含保护时变成了链接器问题。【参考方案2】:

切勿在头文件中定义数据或函数(静态内联除外)。 始终在 .c 源文件中执行此操作。如果您想让它们在其他编译单元中可见,请在头文件中将它们声明为 extern

如果您在许多编译单元中包含相同的 .h 文件,然后将它们链接在一起,则防护不会保护您。

【讨论】:

【参考方案3】:

包含保护将保护您多次包含相同的包含,它将使包含中的定义仅在第一个包含点处理一次。这意味着您在保护标记之间所做的声明不会重复,因此不会产生关于双重定义的错误。这通常不是

extern type_of_variable variable_name;

您可以多次执行此操作而不会引起编译器的任何投诉...它更多地与类型声明或标题中包含的static 函数实现有关。

但是您为什么不发布一个完整的可编译代码示例并展示您正在尝试什么,以及为什么它不起作用。从您的问题中,我无法猜测您假装做什么,如果您的情况不起作用(好吧,您引用了可能会有答案的其他案例,那么为什么不在那里使用答案?有什么问题那里给出的答案?)

请发布一个让您担心的有效示例,并准确解释为什么其他帖子不能解决您的问题(如果您有的话)

认为其中一个问题没有显示重复包​​含的实际内容,也没有显示实际的变量定义。另一个是 8 年前关闭的问题,由于某种原因尚未重新打开。所以你冒着同样的风险(而且我确实冒着因为这个实际上没有回答你的问题的答案而被否决的风险,因为你实际上并没有问你正在发生什么事情——我不知道你的问题如果你有一个实际的问题或没有)。

【讨论】:

我想我同意您的评估 - 您的回答似乎是元评论而不是答案。您答案的前几段是可以的,但其余的可能最好作为问题下的 cmets。无论如何,我会看看我是否可以结束这个问题 - 感觉相当模糊。

以上是关于C:外部变量声明并包含守卫[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

ruby在一个文件中定义的变量,如何在下一个文件中使用该变量

当在同一个文件中同时存在变量声明及其外部声明时,这意味着啥?

C ++接收纯虚函数错误,“变量或字段'x'声明为void”[关闭]

如何在其正文中解开守卫声明?

c 中结构体变量 如何在其他c文件中声明

c语言怎样声明和定义全局变量