如下代码:
Program www_fcode_cn Implicit None integer func write(*,*) 'result=',func() End Program www_fcode_cn Integer Function func() write(*,*) '程序内write' func = 1 End Function func第4行代码,输出 'result=' 后,write 屏幕语句尚未结束,再调用 func 函数。
而该函数内部又执行了 write 屏幕输出。
此时,就会发生这种错误。IVF 会出现如下运行时错误:
实际上,绝大多数编译器同样会把 write 语句作为函数来对待。当 write 尚未结束时,再次执行它,就会因被占用而出错。我们可以想象一下,删除一个正在运行的 exe 程序。
这种错误比较隐蔽,很多时候不易被发现。为了避免它,我们应养成良好的习惯,function 内部尽量不使用 write 屏幕输出。如遇错误,可返回错误代码。例如,返回 1 表示成功,返回 0 表示失败等等。再由调用者根据返回值来输出错误信息。
另外,上例也可以用其他方式来避免错误发生。比如先执行 func,得到结果后,存入某变量 i ,再输出 'result=' 和变量 i。
Program www_fcode_cn Implicit None integer func , i i = func() write(*,*) 'result=',i End Program www_fcode_cn
彭国伦的《Fortran95程序设计》一书,第八章例子 ex0829 中,其实存在这个问题,但彭老师考虑到当前话题并不在于输出占用,于是未详细介绍此问题。
其代码如下:
program ex0829 implicit none integer :: n integer, external :: fact write(*,*) 'N=' read(*,*) n write(*, "(I2,'! = ',I8)" ) n, fact(n) stop end recursive integer function fact(n) result(ans) implicit none integer , intent(in) :: n integer, save :: count = 1 integer :: localcount, temp ! 局部变量 localcount = count count = count+1 write(6,"(I2,'th enter, n=',I2)") localcount, n if ( n < 0 ) then ! 不合理的输入 ans = -1 ! 随便设定一个值 write(6,"(I2,'th exit, n=',I2,' ans=',I8)") localcount, n, ans return ! n不合理, 直接return else if ( n <= 1 ) then ans = 1 write(6,"(I2,'th exit, n=',I2,' ans=',I8)") localcount, n, ans return ! 不用再向下递归了, return end if ! 会执行到这, 代表n>1, 从n*(n-1)!来计算n! temp = n-1 ans = n * fact(temp) write(6,"(I2,'th exit, n=',I2,' ans=',I8)") localcount, n, ans return end
第7行代码调用 fact 函数,而该函数内部又有 write 语句。但读者编译这段代码时,却没有发生IO占用错误。
这是因为,在 fact 函数中,所有 write(*,...) 被换成了 write(6,...),这也正是很多读者对此处的疑问。为何写为 6,而不是 *
在 Visual Fortran 系列编译器上,6 这个通道号,代表着屏幕输出,所以,它能够正常输出。但是,并非所有编译器都是这样规定,各编译器可自行规定10以下的通道号作为保留。例如 Silverfrost Ftn95 编译器,以 -1 作为屏幕输入输出。
PS:笔者建议大家在书写代码时,不要使用10以下的文件通道号。Open 语句等,均使用 10 以上的通道号。
事实上,彭老师是通过函数内使用 6,而主程序使用 * 输出,来避免两者的占用冲突。如果把子程序改为 * 输出,而主程序改为 6 输出,也可同样避免这个错误。但这只能在 Visual Fortran 编译器上可以正常工作。
笔者认为,更合理的方式是,主程序和 fact 函数均使用 * 输出。而将代码第 7 行改为:
i = fact(n) write(*, "(I2,'! = ',I8)" ) n, i
当然了,最理想的情况,还是避免在 function 函数中使用屏幕输出。