C:关于翻译单元的澄清

7
如果我们有两个 .c 文件和一个 .h 文件: main.c sub.c sub.h,其中: main.c
#include "sub.h"
...

sub.c

#include "sub.h"
...

我们可以使用以下方式之一编译程序:i)
gcc -o a.out main.c sub.c

或者 ii)

gcc -c main.c
gcc -c sub.c
gcc -o a.out main.o sub.o

鉴于这种情况,预处理器的输出是否为一个或两个翻译单元?
我感到困惑的原因是:main.c包括sub.h,这意味着预处理器将输出一个编译单元。另一方面,在创建可执行文件之前,有两个对象文件被创建,即main.o和sub.o,这让我认为"两个源文件因此有两个翻译单元"。
我哪里理解有误?或者说我犯了什么错误?
2个回答

6
考虑到生成可执行文件是一个两步过程:首先对每个翻译单元进行编译以生成目标文件,我们称之为编译器。其次,将目标文件链接在一起以生成可执行程序,我们称之为链接器。
“翻译单元”是第一步的问题。翻译单元是编译开始的每个文件(即传递给编译器的文件)。在大多数集成开发环境中,有规则声明每个扩展名为 .c .cpp 的文件作为输入传递给编译器,而其他文件则不是。因此,具有扩展名.h.hpp.txt的文件通常不会直接传递给编译器。
在您的示例中,main.csub.c可能是翻译单元,而sub.h本身不是翻译单元(它仅在其他翻译单元中被“包含”并在其编译过程中考虑)。
因此,您将获得两个目标文件,每个翻译单位一个。这两个目标文件随后由链接器考虑。
请注意,甚至.h文件可能包含完整的程序;但除非您配置环境,使该.h-文件单独编译,否则它不会生成目标文件。

4
以下是关于C标准的说明:
一个源文件连同通过预处理指令#include包含的所有头文件和源文件被称为预处理翻译单元。经过预处理后,预处理翻译单元被称为翻译单元。[...] 先前翻译的翻译单元可以单独或者在库中保留。程序的不同翻译单元通过(例如)调用具有外部链接标识符的函数、操作具有外部链接标识符的对象或操作数据文件来进行通信。翻译单元可以分别翻译,然后稍后链接以生成可执行程序。
(来源:C99草案标准,5.1.1.1 §1)
因此,在你们两个例子中,都有两个翻译单元。其中一个来自编译器对main.c及其通过#include指令包含的所有内容(即sub.h和可能的和其他头文件)进行预处理,另一个则是编译器对sub.c进行相同的操作。
从第一个例子到第二个例子的区别在于,在后者中,你明确地将“不同的已翻译翻译单元”存储为目标文件。
注意,没有规则将一个目标文件与任意数量的翻译单元相关联。GNU 链接器是可以将两个 .o 文件合并的例子。
据我所知,标准并未指定源文件的扩展名。尽管如此,在实践方面,您可以自由地将 .c 文件包含到其他文件中,或将整个程序放置在 .h 文件中。使用 gcc,您可以使用选项 -x c 强制将 .h 文件视为翻译单元的起始点。
这里做出的区分是:
一个源文件以及所有通过预处理指令 #include 包含的头文件和源文件 [...]
这是因为头文件不一定是源文件。同样,在#include指令中,<...>的内容不一定是一个有效的文件名。编译器如何使用命名的头文件<...>"..."是由具体实现定义的。

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