在C语言中包含头文件的好奇心

3
我对包含文件及其如何被管理(使用GCC)有以下好奇:假设我有一个源文件foo.c和三个头文件foo.h、foo_cfg.h和foo_int.h。
在foo.c中:
#include "foo.h"  
#include "foo_int.h" 

foo.h 文件中:
#include "foo_cfg.h" 

foo_cfg.h 文件中:
/* no inclusions */   
#define FOO BAR

foo_int.h 文件中:
/* no inclusions */ 
#define BAR 0U

我很好奇为什么编译会成功。 foo_cfg.h 文件不应该抱怨它不认识 BAR 标记吗?
此外,我还有另一个源文件bar.c,它只包括foo.h文件,但仍然可以正常工作。
备注:这是一个具有复杂构建环境的项目,我没有太多细节。除了为头文件指定位置外,构建环境是否还有其他影响?
如果我的问题真的很愚蠢或者我忽略了什么,请原谅我。
2个回答

6

不,没关系。

你看,预处理器根本不关心BAR是否被定义。它只是在源代码中用BAR替换字符串FOO,而实际上并不关心此时它是否被定义。

接下来,在实际的.c文件(编译开始的地方),两个头文件都被包含,因此编译器看到了两个替换:FOO -> BAR和BAR -> 0U。所以它成功地应用了这两个替换。

头文件永远不会单独编译,它们总是作为包含该头文件的.c文件的一部分进行编译。(预处理器只是假装将头文件的内容粘贴到#include所在的位置。)所以,对于预处理器来说,你的foo.c文件看起来像这样:

/* no inclusions */
#define FOO BAR
/* no inclusions */
#define BAR 0U
/* the rest of the file... */
/* for example: */
unsigned int i = FOO;

在预处理之后,编译器只看到这个:

/* no inclusions */
/* no inclusions */
/* the rest of the file... */
/* for example: */
unsigned int i = 0U;

(不太确定,也许预处理器也会删除注释。)


编辑:
确实,如@pmg所提到的,预处理器会将注释替换为空格,因此馈送给编译器的真正预处理文本只是

_
_
_
_
unsigned int i = 0U;

(这里的_表示空格)

1
你可以使用 gcc -E file.c 命令输出在所有预处理完成后的文件,但是如果包含任何标准头文件,这可能会变得非常冗长。 - BoBTFish
1
@Vlad:是的,预处理器在第三翻译阶段删除注释。请参考标准中的5.1.1.2章节。 - pmg
@pmg:谢谢!标准引用始终是信息的最终来源。我会更新我的答案。 - Vlad

2
为了补充Vlad的答案:
预处理器宏在使用时才会被扩展,而不是在定义时已经扩展了。 所以当你写下 "#define FOO BAR" 时,它所做的只是记住 FOO=BAR。 当你写下 "#define BAR 0U" 时,它会记住 BAR=0U。
现在,当代码中看到 "FOO" 时,它会被替换为 "BAR",随后立即用 "0U" 替换 "BAR"。 "#define FOO" 和 "#define BAR" 在源代码中出现的顺序并不重要。重要的是,在第一次看到 "FOO" 时,这两个定义都已经完成。

啊哈!这就是为什么“bar.c”没有抱怨没有包含“foo_int.h”的原因,因为实际上它没有使用任何依赖于“foo_int.h”的定义。现在我恍然大悟了! - Mircea Ionica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接