Fortran学习笔记6(函数、子程序)


    • 子程序Subroutine
    • 自定义函数Function
    • 全局变量COMMON
    • BLOCK DATA


程序代码中,常常会在不同的地方重复用到某一功能和重复某一代码,这个时候就要使用函数。函数包括内嵌函数,自定义函数,子程序


子程序Subroutine

子程序之所以叫子程序,是因为在没有经过调用之前,它不会被执行。(当然,如果把编译器对主函数的调用也看做同样的话则另当别论。)

包含子程序的程序的格式:

program main 
......
......
call sub_1(...)
......
call sub_2(...)
......
end program main

subroutine sub_1(...)
...
end subroutine sub_1

subroutine sub_2(...)
...
end subroutine sub_2

主程序并不一定要放在最开始,它可以放在程序的任何地方(但是最好放在最开始或者结尾,如果你放在了一大段程序的中间……何苦为难自己!)子程序之间也可以互相调用。甚至可以自己调用自己,这个算法就叫递归,但是Fortran77不支持。调用语句为call

有关子程序,一个很重要的概念是,子程序独立的拥有属于自己的变量申明。主程序和子程序,不同的子程序之间的变量名可以相同,他们互不干扰。除了变量独立,子程序还独立拥有自己的行代码

在子程序的调用中会涉及到参数传递。Fortran中的参数传递使用的是传址调用。也就是说,实参和形参公用一块内存,两者改变其中之一,另一者也会发生变化,(其实是形参暂时的借住在实参家里。)


自定义函数Function

自定义函数的运行和上面的子程序没有什么两样,也需要调用才能被执行,也可以独立声明变量,参数传递的方法和子程序相同。尽管如此,自定义函数和子程序还是有两点不同:

  1. 调用自定义函数前要声明。
  2. 自定义函数调用后会返回一个数值

下面是一个简单的程序:用牛顿法来解方程。

program main
    implicit real*8(a-z)
    external f,g
    integer::max
    integer::i,k=1
    real*8::x0,x1,x2,dx,tol,f,g
    open(unit=11,file='fin.txt')
    open(unit=12,file='fout.txt')
    read(11,*)x0,max,tol

    do 
        x1=x0-f(x0)/g(x0)
        dx=abs(x1-x0)
        if(dx<=tol) exit
                k=k+1
        if(k>=max) exit
                x0=x1
    end do

    write(12,*)x0
    end program main

function f(x)
      implicit none
      real*8::f,x
      f=x**3+2*x**2+10*x-20
end function

function g(x)
      implicit none
      real*8::g,x !,dx
      g=3*x**2+4*x+10
end function

下面是一个更简单的例子:(摘自彭国伦《Fortran95程序设计》一书)

program main
    implicit  none
    real::a=1,b
    real add
    add(a,b)=a+b             
! 简单的子程序直接写在主程序里面,并且不用call调用。并且这样的函数只能在本主函数中使用,其他地方不能调用。
    write(*,*)add(a,3.0) 
    stop
end program

写函数的时候请注意,尽量不要去改变传入的参数的数值,最好另开辟一个内存来存储要用的应变量,而不要随意去改变传入的自变量。


全局变量COMMON

不同的程序之间,也就是在不同的函数之间,除了可以通过传递参数的方法来共享内存,还可以通过全局变量来让不同程序中声明出来的变量使用相同的内存位置。

program main
    implicit none
    integer::a,b
    common a,b
    a=1
    b=2
    call sub()
end program

subroutine sub()
    implicit none
    integer::num1,num2
    common num1,num2
    write(*,*)num1,num2
    return
end subroutine sub

由于全局变量使用的“地址对应”的方法在程序中共享数据,所以在程序设计时常常会出现一些不方便,比如在主程序中声明了6个全域变量,而你在子程序中只需要使用第六个全域变量,但是你不得不把前五个都写出来。这样很麻烦,一个幼小的解决办法是将全域变量分区。

program main
    implicit none
    integer::a,b,c,d
    common/group1/ a,b
    common/group2/ c,d
    a=1
    b=2
    c=3
    d=4
    call sub_1()
    call sub_2()
end program

subroutine sub_1()
    implicit none
    integer::num1,num2
    common/group1/ num1,num2
    write(*,*)num1,num2
    return
end subroutine sub_1

subroutine sub_2()
    implicit none
    integer::num1,num2
    common/group2/ num1,num2
    write(*,*)num1,num2
    return
end subroutine sub_2

BLOCK DATA

关于COMMON设置初值,不能直接在子程序或者主程序中使用data来设置初值,要在block data程序模块中使用data命令来设置初值。BLOCK data很像一段子程序,它也是一段独立的程序模块,也拥有自己的变量声明,不过它不需要别人调用就可以自己执行。事实上,这段程序会在主程序执行前生效,不过它的功能只在于设置全局变量的初值,不能有其他执行命令出现。具体语法如下:

 block data name    !   name可以省略
    implicit none       !   最好不要省略
    integer...               !  声明变量
    real...
    common ...          ! 把变量放在公共空间
    common/group1/...         !   公共空间分区  
    data var1,var2,...,varn/value1,value2,...,valuen/     !   data语句赋初值
    ...
    ...
    end block data name              ! 可以只写end或end block data

还有一点要注意,全局变量不能声明成常量。所以block data中不能出现parameter。

实际的例子如下:

 program main
    implicit none
    integer::a,b
    common a,b
    integer::c,d
    common/group1/c,d
    integer::e,f
    common/group2/e,f

    write(*,*)a,b,c,d,e,f
    stop
end  program main

 block data name    !   name可以省略
    implicit none       !   最好不要省略
    integer::a,b
    common a,b
    data a,b/1,2/
    integer::c,d
    common/group1/ c,d
    data c,d/3,4/
    integer::e,f
    common/group2/e,f
    data e,f/5,6/
    end block data name              ! 可以只写end或end block data

小结

COMMON语法在数据共享式会出现很多限制和不足,建议不要使用而采用module语句来实现数据共享。