最近,我遇到了由Fortran数组的自动初始化(由IDE Visual Studio)引起的错误。所以我编写了一个简单的例程来对其进行测试:
SUBROUTINE test
IMPLICIT NONE
INTEGER :: a, u(2)
a = 1
WRITE(*,'(a,2i5)') 'original :', u(1), u(2)
u(1) = a + 1
WRITE(*,'(a,2i5)') 'after manip :', u(1), u(2)
END SUBROUTINE test
如果我们两次调用此子例程,例如:
PROGRAM main
IMPLICIT NONE
INTEGER :: i
DO i=1,2
CALL test
END DO
END PROGRAM main
我们将得到如下输出:
original : 0 0
after manip : 2 0
original : 2 0
after manip : 2 0
因此我们可以发现,即使不进行初始化,fortran中的数组也将自动设置为零。但是,如果我们对其元素执行某些操作,则在程序退出子例程时,不会从堆栈中删除此元素,并且该元素将被保留并在下一次调用中重用。 也许这个问题没有意义,因为它被要求初始化变量。.但是我只是对背后的机制感到好奇,希望将其公开给其他人 提前致谢。 杜
答案 0 :(得分:4)
在每次调用时都不会自动初始化局部变量。
在子程序的每次调用中,所有局部变量都是 undefined ,除非变量具有SAVE
属性。通过在声明Fortran assignment on declaration and SAVE attribute gotcha
因此,如果变量不是SAVE
,则在调用子例程时未定义。使用存储在其中的值是无效的。那里可以有任何东西。 由于在调用相同的子程序时堆栈通常以相同的方式排列,因此本地变量可能恰好位于旧值所在的相同内存地址处。除非被其他子程序重写,否则旧值可能仍然存在。但这只是巧合。
如果您希望变量在子例程启动时具有某个定义的值,请将其设置为SAVE
。但是,这对于递归子例程和线程来说可能是个问题。
或在子程序的第一个可执行语句中定义变量。
关于标题。 Visual Studio本身没有Fortran。您可能正在使用几个编译器。假设您使用的是Intel Visual Fortran,请参见How to initialize all local variables to zero。
考虑:
PROGRAM main
IMPLICIT NONE
INTEGER :: i
DO i=1,2
CALL test
call test2
END DO
END PROGRAM main
SUBROUTINE test
IMPLICIT NONE
INTEGER :: a, u(2)
logical, save :: called = .false.
if (.not. called) then
called = .true.
u = 0
end if
a = 1
WRITE(*,'(a,2i5)') 'original :', u(1), u(2)
u(1) = a + 1
WRITE(*,'(a,2i5)') 'after manip :', u(1), u(2)
END SUBROUTINE test
SUBROUTINE test2
IMPLICIT NONE
real :: dummy(20)
call random_number(dummy)
call test
END SUBROUTINE test2
导致:
> gfortran stack.f90
> ./a.out
original : 0 0
after manip : 2 0
original :32766 0
after manip : 2 0
original :**********
after manip : 2*****
original :32766 0
after manip : 2 0
或者考虑一下:
call s1
call s2
end
subroutine s1
use iso_fortran_env, only: int64
integer(int64) :: i
i = 2314885733423007048_int64
end subroutine
subroutine s2
character*8 :: ch
print *,ch
end subroutine
输出:
> gfortran test2.f90
> ./a.out
HELLO