对象链接#

除了最简单的程序之外,几乎所有程序都是由不同的部分构建而成的。我们将更详细地研究这种情况。

这是一个用于制表函数的通用程序(源代码在“tabulate.f90”中)

program tabulate
    use user_functions

    implicit none
    real    :: x, xbegin, xend
    integer :: i, steps

    write(*,*) 'Please enter the range (begin, end) and the number of steps:'
    read(*,*)  xbegin, xend, steps

    do i = 0, steps
        x = xbegin + i * (xend - xbegin) / steps
        write(*,'(2f10.4)') x, f(x)
    end do
end program tabulate

请注意 use 语句——这将是我们定义函数 f 的地方。

我们希望使程序通用,因此请将特定的源代码(函数 f 的实现)与通用源代码分开。有几种方法可以实现这一点,但其中一种是将其放在不同的源文件中。我们可以向用户提供通用程序,让他们提供特定的源代码。

为了举例说明,假设该函数在源文件“functions.f90”中实现为

module user_functions
    implicit none
contains

real function f( x )
    real, intent(in) :: x
    f = x - x**2 + sin(x)
end function f

end module user_functions

要使用此特定函数构建程序,我们需要编译两个源文件并通过链接步骤将其组合成一个可执行程序。因为程序“tabulate”依赖于模块“function”,所以我们需要先编译包含模块的源文件。执行此操作的命令序列为

$ gfortran -c functions.f90
$ gfortran tabulate.f90 functions.o

第一步编译模块,生成目标文件“functions.o”和模块中间文件“user_functions.mod”。此模块文件包含编译器确定函数 f 在此模块中定义及其接口所需的所有信息。此信息非常重要:它使编译器能够检查您是否以正确的方式调用该函数。您可能犯了一个错误,并使用两个参数而不是一个参数调用了该函数。如果编译器对函数的接口一无所知,那么它就无法检查任何内容。

第二步以以下方式调用编译器:

  • 它编译文件“tabulate.f90”(使用模块文件);

  • 它调用链接器将目标文件 tabulate.o 和 functions.o 组合成一个可执行程序——默认名称为“a.out”或“a.exe”(如果需要不同的名称,请使用选项“-o”)。

您通常看不到的是,链接器在此链接步骤中还会添加许多额外的文件,即运行时库。这些运行时库包含所有“标准”内容——执行屏幕输入和输出的低级例程、sin 函数等等。

如果要查看详细信息,请添加选项“-v”。这指示编译器报告所有详细步骤。

最终结果(可执行程序)包含已编译的源代码和使其正常工作的各种辅助例程。它还包含对所谓的动态运行时库的引用(在 Windows 中:DLL,在 Linux 中:共享对象或共享库)。如果没有这些运行时库,程序将无法启动。