Vinculando os objetos#

Quase todos os programas, exceto os mais simples, são construídos por diferentes peças. Nós vamos examinar tal situação em detalhe.

Aqui um programa geral para tabular uma função (código fonte em «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

Note a declaração use - ela estará onde definimos a função f.

nós queremos fazer o programa generalista, então mantenha o código fonte específico - a implementação da função f - separado do código fonte geral. Há várias formas de se conseguir isso, uma sendo colocar em um arquivo fonte diferente. Podemos dar o código generalista para um usuário e eles proverão um código fonte específico.

Presuma para o contexto do exemplo que a função é implementada em um código fonte «functions.f90» como:

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

Para construir o programa com a função específica, precisamos compilar dois códigos fontes e combiná-los com a fase de link em um programa executável. Por conta do programa «tabulate» depender de um módulo «function», precisamos compilar o código fonte contendo o nosso módulo primeiro. Uma sequência de comandos para fazer isso é:

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

O primeiro passo compila o módulo, resultando em um arquivo objeto «functions.o» e um arquivo módulo intermediário, «user_functions.mod». Esse arquivo módulo contém toda a informação que o compilador precisa para determinar que a função f é definida nesse módulo e qual é sua interface. Essa informação é importante: Isso permite que o compilador cheque que você chama a função da forma correta. Pode ser que você tenha cometido um erro e chamado a função com dois argumentos ao invés de um. Se o compilador sabe nada sobre a interface da função, então ele pode checar nada.

O segundo passo invoca o compilador de tal forma que:

  • ele compila o arquivo «tabulate.f90» (usando o arquivo módulo);

  • isso invoca o linker para combinar os arquivos objeto tabulate.o e functions.o em um programa executável - com o nome padrão «a.out» ou «a.exe» (se você quer um nome diferente, use a opção «-o»).

O que você não vê no geral é que o linker também adiciona um número de arquivos extra nessa fase de link, as bibliotecas de tempo de execução. Essas bibliotecas de tempo de execução contém todas as «coisas padrão» - rotinas de baixo nível que fazem a entrada e saída da tela, a função sin e muito mais.

Se você quer ver mais detalhes, adicione a opção «-v». Isso instrui o compilador a apresentar todos os passos em detalhe.

O resultado final, o programa executável, contém o código fonte compilado e várias rotinas auxiliares que o fazem funcionar. Ele também contém referências para as chamadas bibliotecas de tempo de execução dinâmicas (no Windows, DLLs, no Linux: objetos compartilhados ou bibliotecas compartilhadas). Sem essas bibliotecas de tempo de execução o programa não iniciará.