在C++中,“翻译单元”是什么意思?

326
我现在正在阅读Scott Meyers写的《Effective C++》,看到了一个术语叫做“翻译单元”。
请问有人能给我解释一下以下问题吗:
1. 它到底是什么? 2. 在使用C++编程时,何时应该考虑使用它? 3. 它只能用于C++,还是可以用于其他编程语言?
也许我已经在使用它,只是不知道这个术语而已...

1
如果您已经包含了头文件,那么您已经在使用翻译单元。这是一个用于参考而不是 C++ 构造的术语。 - talekeDskobeDa
11个回答

340

来自这里:(wayback machine链接)

根据C++标准 (wayback machine链接): 翻译单元是C++中编译的基本单位。它由单个源文件的内容以及直接或间接包含在其中的任何头文件的内容组成,减去使用条件预处理语句忽略的那些行。

单个翻译单元可以编译为对象文件、库或可执行程序。

翻译单元的概念在一些场景中被经常提到,例如一次定义规则和模板。


23
这个术语仅在 C/C++ 中使用吗? - dekuShrub
6
事实上,不是这样。比如在Rust中,翻译单元被称为crate,在C++中同样的东西会被称为整个库。这个术语本身是通用的,但它肯定是从C语言开始的。 - user11877195
2
新的参考资料大致说明了这个答案所述的内容:https://zh.wikipedia.org/wiki/%E7%BF%BB%E8%AF%91%E5%8D%95%E5%85%83%E6%A8%A1%E5%9D%97 - Gabriel Staples
在编译之前能否查看翻译单元的内容? - 425nesp
@425nesp GCC之前有-fdump-lang-raw选项,但最近已被删除。其他编译器/工具也有类似的选项: https://stackoverflow.com/q/39140779/159145 - Dai

89

3
包括头文件。即使不生成任何代码,编译器也会处理头文件。 请参阅JeffH的预处理器注释,定义“编译器所看到的所有内容”是一个很好的定义。 - Marco van de Voort
16
你可以编译扩展名为“.h”的文件。文件名并不重要,内容才是关键。例如,如果“foo.h”的内容为“int main() { }”,你也可以将其编译。 - Johannes Schaub - litb
@LightnessRacesinOrbit:是的,我想说的是直接将头文件编译为TU而不是通过包含间接地将其编译为TU是不正规的。删除第一个评论是因为它是完全错误的,保留第二个评论是为了给我们的新评论提供背景。 - GManNickG
1
@GManNickG:“.h文件通常不直接提供给编译器。” - Lightness Races in Orbit
2
@JohannesSchaub-litb 我想你的意思是链接,而不是编译。只要文件是正确的C/C++文件且所有名称都已定义,就可以编译任何文件。编译头文件是无用的,因为头文件的整个目的是被包含(读取复制)到源文件中,所以当您编译包含它的源文件时,它们已经被编译了。我猜你想说的是,如果一个文件没有main函数,那么你不能从它创建可执行文件。 - pooya13
那么这是目标文件吗? - Alec Mather

36

这是一个难以明确回答的问题。C++标准规定:

程序文本在国际标准中被称为源文件,由多个单元组成。一个源文件与所有头文件(17.4.1.2)和通过预处理指令 #include 包含的源文件(16.2)一起,减去任何条件包含(16.1)预处理指令跳过的源代码行,称为一个翻译单元。[注意:C++程序不需要同时全部翻译。]

因此,对于大多数目的而言,翻译单元是一个单独的C++源文件和它通过预处理器 #include 机制包含的头文件或其他文件。

关于您的其他问题:

  1. 在使用C++编程时应该考虑什么时候使用它?

你不能不使用它——翻译单元是C++程序的基础。

  1. 它只与C++相关,还是可以与其他编程语言一起使用?

其他编程语言也有类似的概念,但它们的语义会略有不同。例如,大多数其他语言不使用预处理器。


1
我不知道这是否能澄清问题。这可能是一个有些模糊的领域 - 例如,从我引用的标准段落中并不清楚是否允许预编译头文件。 - anon
1
@GMan,这就是你必须非常小心关于单一定义规则的地方。如果在包含该类之前,在不同的翻译单元中使用略有不同的定义来包含一个类,会导致该类具有不同的代码,从而引起未定义的问题。 - Matt Price
6
请注意标准使用的两个术语:“头文件”和“源文件”。 “头文件”仅用于标准库。被某些代码包含的用户文件在标准中不被称为“头文件”,而被称为“源文件”。标准并不知道我们这些可怜的C++程序员想出的“.h”和“.cpp”的区别 :) - Johannes Schaub - litb

7

这本书已经讲得很清楚了。当Meyers提到“翻译单元”时,他指的是源代码文件。


1
不是的。如果他在谈论源代码,他会说源文件。翻译单元是通过编译源代码生成的。请注意这个明显的区别。这是“翻译后”的源代码。 - Dan
4
不是的。翻译单元指的是经过包含后可以被编译的源文件,也就是编译前预处理器的输出结果。 - Ed S.
预处理器恰好是编译过程的一部分。我并不是想挑毛病,但当你谈论“翻译单元”时,已经预处理和未经预处理的之间存在明显的区别。它们不是可互换的名词。这就是我的观点。 - Dan
1
事实上,尽管C++标准称之为“翻译单元”,但“翻译单元”通常用于传达编译代码的单个“单元”的概念。实际上,根据微软编译器团队的说法,您可以直接链接“翻译单元”。http://msdn.microsoft.com/en-us/library/vstudio/bxss3ska(v=vs.100).aspx - Dan
1
我们是想成为“C++标准”纳粹,还是帮助人们与其他行业进行沟通?我知道这是一个C++线程,所以我不会深入讨论xcode称之为tu的内容,或者所有其他定义。 - Dan
4
@Dan:翻译单元是标准所称。我不太关心随意编译器开发者的观点。有趣的是,那个人挖出一个将近五年的旧帖子来挑刺,并告诉我我的定义是错误的,然后反过来称呼我为“语言纳粹”,真让人嗟叹,继续前进吧,和你打交道真累。 - Ed S.

6
除了ODR之外,翻译单元在未命名的命名空间定义中也很重要,它替换了“静态”原有的用法。

5

翻译单元是传递给编译器的代码。通常,这意味着在.c文件上运行预处理器后的输出。


3

C和C++程序由一个或多个源文件组成,每个源文件包含程序的一部分文本内容。一个源文件与它的包含文件(使用#include预处理器指令包含的文件),但不包括被条件编译指令(如#if)移除的代码部分,被称为“翻译单元”。


1
根据MSDN的说法:C和C++程序由一个或多个源文件组成,每个源文件包含程序的一些文本。一个源文件连同其包含文件(使用#include预处理器指令包含的文件),但不包括被条件编译指令(如#if)移除的代码部分,称为“翻译单元”。

0
每个cpp/c(实现)文件都将被转换为一个翻译单元(即,对象文件(.obj)),cpp文件中的头文件将被替换为头文件中的实际文本。

0

正如其他人所说,翻译单元基本上是预处理后源文件的内容。它是语言语法中最顶层的产物;只有在编写C或C++编译器时才需要担心它。


1
只有在编写C或C++编译器时才需要担心这个问题。我不同意:程序员经常需要了解编译器正在做什么。因此,例如,您需要知道翻译单元是什么,以便理解Effective C++中第5项的重要观点:“在不同的翻译单元中定义的非局部静态对象的初始化相对顺序是未定义的”。 - Channing Moore

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