34

去年,我在一所研究型大学工作,开始使用 Fortran 进行编程。我之前的大部分经验都是使用 PHP 或旧 ASP 等 Web 语言,所以我是编译语句的新手

我正在修改两个不同的代码。

在创建可执行文件之前,有一个明确的语句从模块创建 .o 文件(例如 gfortran -c filea.f90)。

另一种直接创建可执行文件(有时会创建 .mod 文件,但不创建 .o 文件,例如 gfortran -o executable filea.f90 fileb.f90 mainfile.f90)。

  • 是否有理由(可能除了 Makefiles 之外)一种方法优于另一种方法?
4

5 回答 5

32

首先编译成目标文件称为单独编译。有很多优点和一些缺点。

好处:

  • 易于将目标文件 (.o) 转换为库并稍后链接到它们
  • 许多人可以同时处理不同的源文件
  • 更快的编译(当源没有改变时,你不会一次又一次地编译相同的文件)
  • 目标文件可以由不同的语言源制作,并在以后的某个时间链接在一起。为此,目标文件只需使用相同的格式和兼容的调用约定。
  • 单独编译可以分发静态或共享的系统范围的库(操作系统库、语言标准库或第三方库)。

缺点:

  • 有一些优化(比如优化函数)编译器无法执行,链接器也不关心;然而,许多编译器现在包括执行“链接时间优化”的选项,这在很大程度上消除了这个缺点。但这对于系统库和第三方库来说仍然是一个问题,尤其是对于共享库(不可能优化掉每次运行时可能更改的组件部分,但是 JIT 编译等其他技术可能会缓解这种情况)。
  • 在某些语言中,程序员必须提供某种类型的标头以供其他将与该对象链接的人使用。例如,在 C 中,您必须提供.h与目标文件一起使用的文件。但无论如何,这是一种很好的做法。
  • 在具有基于文本的语言(如 C 或 C++)中,如果更改函数原型,则必须在两个地方进行更改。一次在头文件中,一次在实现文件中。
于 2011-03-12T16:48:46.357 回答
15

当您有一个包含 100 个源文件的项目时,您不希望每次更改时都重新编译所有文件。通过将每个源文件编译成单独的目标文件并仅重新编译那些受更改影响的源文件,您从源代码更改到新的可执行文件所花费的时间最少。

make是用于跟踪此类依赖关系并在发生变化时重新创建二进制文件的常用工具。通常,您设置每个源文件所依赖的内容(这些依赖项通常可以由您的编译器生成 - 以适合的格式make),并让 make 处理创建最新二进制文件的细节。

于 2011-03-12T16:46:59.923 回答
6

.o 文件是目标文件。它是最终程序的中间表示。

具体来说,.o 文件通常具有已编译的代码,但它没有的是所有不同例程或数据的最终地址。

程序在运行之前需要的东西之一是类似于内存映像的东西。

例如。

如果你有你的主程序,它调用了一个例程 A。(这是假的 fortran,我几十年没碰过,所以在这里和我一起工作。)

PROGRAM MAIN
INTEGER X,Y
X = 10
Y = SQUARE(X)
WRITE(*,*) Y
END

然后你就有了 SQUARE 函数。

FUNCTION SQUARE(N)
SQUARE = N * N
END

它们是单独编译的单元。您可以看到,编译 MAIN 时,它不知道“SQUARE”在哪里,它在什么地址。它需要知道,所以当它调用微处理器 JUMP SUBROUTINE (JSR) 指令时,该指令有可去处。

.o 文件已经有 JSR 指令,但它没有实际值。这在链接或加载阶段的后期出现(取决于您的应用程序)。

因此,MAINS .o 文件包含 main 的所有代码,以及它想要解析的引用列表(特别是 SQUARE)。SQUARE 基本上是独立的,它没有任何引用,但同时它还没有关于它在内存中的位置的地址。

链接器将删除所有 .o 文件并将它们组合成一个 exe。在过去,编译后的代码实际上就是一个内存映像。该程序将从某个地址开始并简单地批量加载到 RAM 中,然后执行。因此,在场景中,您可以看到链接器获取两个 .o 文件,将它们连接在一起(以获取 SQUARE 的实际地址),然后它会返回并在 MAIN 中找到 SQUARE 引用,并填写地址。

现代链接器并没有走那么远,并将大部分最终处理推迟到程序实际加载时。但是这个概念是相似的。

通过编译为 .o 文件,您最终会得到可重用的逻辑单元,然后在执行之前由链接和加载过程组合这些逻辑单元。

另一个不错的方面是 .o 文件可以来自不同的语言。只要调用机制是兼容的(即参数是如何传递给函数和过程的),那么一旦编译成.o,源语言就变得不那么相关了。例如,您可以将 C 代码与 FORTRAN 代码链接、组合。

在 PHP 等中,该过程是不同的,因为所有代码在运行时都加载到单个图像中。您可以考虑 FORTRANs .o 文件,类似于使用 PHPs 包含机制将文件组合成一个大的、有凝聚力的整体。

于 2011-03-12T17:00:57.120 回答
2

除了编译时间之外,另一个原因是编译过程是一个多步骤的过程

多部分过程。

目标文件只是该过程的一个中间输出。链接器最终将使用它们来生成可执行文件。

于 2011-03-12T16:51:10.217 回答
1

我们编译为目标文件,以便能够将它们链接在一起以形成更大的可执行文件。这不是唯一的方法。

也有编译器不这样做,而是编译到内存并立即执行结果。早些时候,当学生不得不使用大型机时,这是标准的。Turbo Pascal 也是这样做的。

于 2011-03-12T17:50:33.213 回答