usr*_*usr 17 c language-lawyer
翻译单元的常用定义是预处理之后(头文件包含,宏等与源文件一起).这个定义相当清楚,C标准5.1.1.1,C11说:
AC程序不需要全部同时翻译.该程序的文本保存在本国际标准中称为源文件(或预处理文件)的单元中.源文件以及通过预处理指令包含的所有头文件和源文件
#include称为预处理转换单元.在预处理之后,预处理翻译单元被称为翻译单元.
更仔细地阅读第一句话:
AC程序不需要全部同时翻译.
这意味着(对我来说),C程序可以在同一个程序中进行翻译,而不必将它们分成多个预处理源文件.同样在同一段的末尾,标准说:
翻译单元可以单独翻译,然后链接以产生可执行程序.
可以(通常是)解释为编译单个目标文件,然后最终链接它们以生成单个可执行程序.但是,如果可以从上述语句中提出问题并询问:这是否意味着实现可以自由地将多个源文件视为单个翻译单元,尤其是对于以下调用:
gcc file1.c file2.c -o out
Run Code Online (Sandbox Code Playgroud)
编译器可以访问整个源?
特别是,如果某个实现将file1.c+ file2.c(上述)视为单个翻译单元,是否可以将其视为不合格?
M.M*_*M.M 13
在第二行中你引用了:
该程序的文本保存在本国际标准中称为源文件(或预处理文件)的单元中
如果有两个源文件,则有两个预处理文件,因此有两个预处理转换单元,因此有两个转换单元.一个对应于每个源文件.
该标准未定义源文件.我想编译器可以说"我正在编写我自己的'源文件'版本,通过声明file1.c并且file2.c毕竟不是源文件!" 并将它们连接起来,但这与程序员的期望不一致.我想你会很难说这file1.c不是一个源文件.
但是,如果可以从上述语句中提出问题并询问:这是否意味着实现可以将多个源文件视为单个翻译单元
不.定义很明确:
甲连同所有的头文件和源文件通过预处理指令#include包含的源文件被称为预处理翻译单元.在预处理之后,预处理翻译单元被称为翻译单元.
翻译单元是预处理一个源文件及其包含的结果.您可能同时翻译两个翻译单元并不意味着您可以将它们视为一个翻译单元.
编译器可以同时自由翻译多个源文件,但它们无法更改其语义.
将几个文件一起翻译可能会更快(因为编译器只启动一次)并且将允许更好的整个程序优化:其他翻译单元中的被调用函数的源代码然后可以在来自其他翻译单元的调用点处获得.编译器可以检查被调用的代码并使用这些信息,就像使用单个翻译单元一样.从gcc 6.3.0手册:
编译器根据程序的知识执行优化.将多个文件一次编译为单个输出文件模式允许编译器在编译每个文件时使用从所有文件中 获取的信息.
可以检查被调用函数是否存在别名,指向对象的事实常量等,使编译器能够执行在一般情况下会出错的优化.
当然,这些功能可以内联.
但是存在(预处理)转换单元的语义(对应于预处理之后的源文件,根据您的标准引用),编译器必须遵守这些语义.@Malcolm提到了一个文件静态变量.我的直觉是,关于声明和声明顺序可能还有其他更微妙的问题.
另一个明显的源代码范围问题是定义.从n1570草案,6.10.3.5:
宏定义持续(独立于块结构),直到遇到相应的#undef指令或(如果没有遇到)直到预处理转换单元结束.
这两个问题都禁止简单的C源文件串联; 编译器必须另外应用一些基本逻辑.
翻译单元表示点C文件.所有意图和目的,包括其相关的点h包括.很少#include指令用于添加其他文件类型或其他点C文件.
静态变量仅在翻译单元中可见.有一些公共函数与外部链接以及许多静态函数和数据项支持是很常见的.所以C转换单元有点像单例C++类.如果编译器没有正确处理静态,则它是不符合的.
通常,为每个转换单元创建一个目标文件,然后通过链接器链接它们.这实际上并不是标准规定的,而是在文件创建和编译成本相对较慢的环境中做事的自然而明显的方法.