包含头文件的困惑

Posted

技术标签:

【中文标题】包含头文件的困惑【英文标题】:Confusion with including header files 【发布时间】:2011-12-03 05:26:02 【问题描述】:

当我包含一个头文件时,比方说,

//myheader.h
#ifndef MY_HEADER_H
#define MY_HEADER_H

//....

#endif

进入,

//mycpp1.cpp
#include "myheader.h"

我被告知的是,当 mycpp1.cpp 包含 myheader.h 时,MY_HEADER_H 被定义,因此任何再次包含它的尝试都将导致错误。

现在,如果我想将它包含在 mycpp2.cpp 中。

//mpcpp2.cpp
#include "myheader.h"

它会被包含,还是在第一次包含时使用相同的声明?

【问题讨论】:

【参考方案1】:

每个文件的预处理器定义是分开的。因此,如果您将#includemyheader.h 分成两个单独的 .cpp 文件,它将被包含两次,而不是一次。每个 .cpp 一个。

【讨论】:

这就是我的猜测......有什么办法可以检查它是否被包含两次? @user974191 你可以在预处理器处理完文件后查看文件,但你为什么要这样做? 你是指目标文件吗?没有什么只是想了解发生了什么。 @user974191 没有。您可能知道,预处理器在编译之前获取每个文件并替换#defines,将#includes 替换为#included 等文件的内容。如果包含文件,您可以在该文件的#include 保护之外放置注释,然后查看预处理器的输出并查看该注释出现了多少次。所以对于 gcc,你会做 gcc -E file.cpp 这会给你预处理器的输出,你可以计算它出现的次数。 @user974191 如何查看预处理器的输出将因每个编译器/平台而异。就像我对 gcc 所说的那样,它有 -E 标志,对于 Visual Studio,它可能是一个项目设置。【参考方案2】:

预处理器定义对于当前编译单元是本地的。

当然有复杂的情况,但重点是:

尝试将 .cpp(源)文件视为不同的实体。 如果你不做真正奇怪的事情,那么 如果您删除所有 .cpp 文件,除了您打扰的文件, 你仍然可以编译,因为在编译阶段不需要 对于定义,您只关心事物的名称(声明)。

所以,一次编译N个源文件本质上是这样的:

[ *.H + SOURCE1.CPP ] --> SOURCE1.O
[ *.H + SOURCE2.CPP ] --> SOURCE2.O
...
[ *.H + SOURCEN.CPP ] --> SOURCEN.O

每一行都是一个不同的编译单元,它 将 SourceX.CPP 和包含的标头呈现到 Object 文件中。 所以我们得到了 N 个单独的东西。

这样,如果您不更改公共标题,那么您不必 重新编译未修改的源文件。当然,如果你修改了一个源文件, 你必须重新编译它。最后,如果你修改了一个公共标题,那么你有 重新编译包含它的每个源文件。 这里不得不提一下,在编译阶段之前,所有 #inlude "filename.ext" 行(预处理器指令)将被替换 包含 filename.ext 文件的确切内容,无论它是什么。

然后链接是一个不同的问题,在那个阶段 目标是从这 N 个目标文件中创建一个文件。 (我再说一遍,这是个简单的例子)

这是链接:

[ SOURCE1.O + SOURCE2.O + ... + SOURCEN.O ]  --> EXECUTABLE.FILE

将受影响的目标文件想象成一袋值和 算法(函数定义)。例如,袋子里的某个地方一定有一个 main 函数(定义),所以链接器肯定会知道什么 执行程序时要做的事情。

希望你明白

猜猜会发生什么,如果将全局函数的定义写入头文件, 然后将它包含在两个单独的编译单元中,然后尝试链接它们。

答案:链接器错误 - 多个定义,因为您可以单独编译它们。

【讨论】:

【参考方案3】:

如果MY_HEADER_H只是定义在mycpp1.cpp中,头文件会包含在mycpp2.cpp中

诀窍的真正用途是:

header1.h 包括 header2.h。 因此,当您在 mycpp.cpp 中同时包含 header1.hheader2.h 时, header2.h 如果您不这样做,将被包含两次。

【讨论】:

【参考方案4】:

正如其他人所说,每个 .cpp 文件包含一次头文件。但我还想提一下这样做的全部目的:

#ifndef MY_HEADER_H
#define MY_HEADER_H
// ..
#end if

是为了防止这引起问题

mycpp1.cpp

#include "myheader.h"
#include "myheader.h"
// ...

我开始时遇到的另一件事是,当您将头文件包含在多个 .cpp 文件中并在其中定义了全局变量时...这就是我要解决的问题...

myheader.h

#ifdef MY_HEADER_H
#define MY_HEADER_H

#ifdef GLOBAL_EXTERN
extern int nGlobalInt;
#else
int nGlobalInt = 282;
#endif

#endif

mycpp1.cpp

#include "myheader.h"
// ...

cout << nGlobalInt;

mycpp2.cpp

#define GLOBAL_EXTERN
#include "myheader.h"
// ...

cout << nGlobalInt;

两个 cpp 文件都将打印 282

【讨论】:

如果没有#ifdef GLOBAL_EXTERN ...,您的编译器会出现重新定义错误。 如果你在cpp文件的一个中定义nGlobalInt并去掉GLOBAL_EXTERN会更直接。 extern 充当该变量实例的前向声明。 @Victor T.,同意...但是对于这个问题的上下文,我认为这个例子会更好。【参考方案5】:

它只会被包含一次。指令 MY_HEADER_H 将在第一次包含时定义,随后对#include myheader.h 的所有尝试都将无效。

预处理器指令适用于所有文件。否则,例如,在 myheader.h 中声明的每个类都将被重新定义。

【讨论】:

以上是关于包含头文件的困惑的主要内容,如果未能解决你的问题,请参考以下文章

C语言包含头文件是啥?

Verilog头文件怎么包含

Keil工程已包含头文件,但仍然提示未定义

c++关于multiset的头文件包含问题。

CString类型要包含啥头文件

c语言怎么包含自己写的头文件?