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

问题描述

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

为什么包含守卫仍然不能解决这个问题?如果我多次包含相同的头文件,我希望包含守卫会避免多次声明我的变量。

解决方法

包含守卫对于防止在单个翻译单元中出现多个声明或类型定义非常有用,即一个 .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 构成一个单独的翻译单元:

公元前:

#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年前关闭的问题,由于某种原因没有重新打开。所以你冒着同样的风险(我确实冒着被这个答案拒绝的风险,这个答案实际上并没有回答你的问题,因为你实际上并没有问你发生了什么——我不知道你的问题如果您有实际问题或没有)。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...