链接对象#

几乎所有程序,除了最简单的,都是用不同的片段组合构建的。我们将更详细地研究这种情况。

这是一个对函数制表的通用程序(源代码在 “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和function.o合并成一个可执行程序–默认名称为 “a.out “或 “a.exe”(如果想要一个不同的名称,可以使用选项”-o”)。

链接器在这个链接步骤中还添加了一些额外的文件,即运行时库,这些通常是不可见得。这些运行时库包含了所有的 “标准 ”东西–执行输入和输出到屏幕的低级例程,sin 函数等等。

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

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