7

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

主程序

#include "sub.h"
...

子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在创建可执行文件之前,让我认为“两个源文件因此两个翻译单元”。

我误解了哪一部分?或者我在哪里犯错误?

4

2 回答 2

6

将可执行文件的生成视为一个两步过程:首先,将每个翻译单元编译为目标文件;让我们称之为编译器。其次,将目标文件链接到一个可执行程序;我们称之为链接器。

“翻译单元”是第一步。翻译单元是编译开始的每个文件(即传递给编译器的文件)。在大多数 IDE 中,有一些规则声明每个带有扩展名的文件.c.cpp作为输入传递给编译器,而其他文件则不是。因此,扩展名为.h,.hpp的文件.txt通常不会直接传递给编译器。

在您的示例中,main.c并且sub.c可能是翻译单元,而sub.h其本身不是翻译单元(它仅“包含”在其他翻译单元中并在其编译过程中考虑)。

所以你得到两个目标文件,每个翻译单元一个。然后链接器会考虑这两个目标文件。

请注意,即使是一个.h文件也可能包含一个完整的程序;但是除非你配置你的环境,这个.h文件是自己编译的,否则它不会生成一个目标文件。

于 2017-01-29T23:56:20.583 回答
4

以下是 C 标准对此的看法:

源文件连同通过预处理指令包含的所有头文件和源文件#include称为预处理翻译单元。经过预处理后,一个预处理翻译单元称为翻译单元。[..] 以前翻译的翻译单元可以单独保存或保存在库中。程序的单独翻译单元通过(例如)调用标识符具有外部链接的函数、标识符具有外部链接的对象的操作或数据文件的操作来进行通信。翻译单元可以单独翻译,然后链接以产生可执行程序。

(来源:C99 标准草案,5.1.1.1 §1)

因此,在您的两种情况下,您都有两个翻译单元。其中之一来自编译器预处理main.c以及通过#include指令包含的所有内容——也就是说,sub.h可能<stdio.h>还有其他头文件。第二个来自编译器对sub.c.

从您的第一个示例到您的第二个示例的不同之处在于,在后者中,您将“不同的翻译翻译单元”明确存储为目标文件。

请注意,没有将一个目标文件与任意数量的翻译单元相关联的规则。GNU 链接器是能够将两个.o文件连接在一起的链接器的一个示例。

据我所知,该标准没有指定源文件的扩展名。尽管如此,在实际方面,您可以自由地将#include一个.c文件放入其他文件中,或者将您的整个程序放在一个.h文件中。您可以使用gcc该选项-x c强制将.h文件视为翻译单元的起点。

这里的区别:

源文件以及通过预处理指令包含的所有头文件和源文件#include[...]

是因为标头不必是源文件。同样,指令<...>中的内容也#include不必是有效的文件名。编译器究竟如何使用命名的头文件<...>并且"..."是实现定义的。

于 2017-01-30T00:23:56.663 回答