为什么在Visual Studio中将Fortran的数组自动初始化为零?但是,当我们对其进行一些操作时,它不会自动初始化吗?

时间:2019-12-16 13:40:40

标签: arrays visual-studio fortran initialization

最近,我遇到了由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中的数组也将自动设置为零。但是,如果我们对其元素执行某些操作,则在程序退出子例程时,不会从堆栈中删除此元素,并且该元素将被保留并在下一次调用中重用。 也许这个问题没有意义,因为它被要求初始化变量。.但是我只是对背后的机制感到好奇,希望将其公开给其他人 提前致谢。 杜

1 个答案:

答案 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