C: 外部变量声明和包含守卫

C: Extern variable declaration and include guards

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

为什么include guard还是解决不了这个问题?如果我多次包含同一个头文件,我希望 include guard 会避免我的变量被多次声明。

包含保护对于防止在单个翻译单元中进行多次声明或类型定义很有用,即一个由自身编译的 .c 文件以及它包含的所有 headers。

假设您有以下 headers 不包含守卫:

a.h:

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没有问题,因为每个都构成了一个暂定定义,一个翻译单元中的多个暂定定义组合起来引用一个object在结果 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.[=25 中定义的 object =]

现在我们 link a.o 和 b.o。 linker 看到 a.o 和 b.o 都包含一个名为 t1 的 object,因此 linking 失败,因为它被定义了多次.

请注意,虽然包含保护可以防止定义在 单个 翻译单元中出现多次,但它不会阻止它在多个翻译单元中出现。

这就是为什么 t1 应该在 a.h 中有一个外部声明:

extern struct test t1;

一个.c文件中有一个non-extern声明。

切勿在头文件中定义数据或函数(静态内联除外)。 始终在 .c 源文件中这样做。如果要使它们在其他编译单元中可见,请在头文件中将它们声明为 extern

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

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

的情况
extern type_of_variable variable_name;

你可以做几次而不会受到编译器的任何抱怨...它更多地与类型声明或 static 函数实现有关 header.

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

拜托,post 一个有效的例子来说明你担心什么,并准确解释为什么其他 post 不能解决你的问题(如果你有的话)

认为其中一题没有显示重复include的实际内容,也没有显示实际的变量定义。而另一个是8年前关闭的问题,不知为何一直没有重新打开。因此,您 运行 面临同样的风险(而我确实 运行 因为这个实际上并没有回答您的问题的答案而被否决的风险,因为您实际上并没有问您正在发生的事情-我不知道你是否有实际问题)。