【问题标题】:lifetime of variables and static arrays in FortranFortran 中变量和静态数组的生命周期
【发布时间】:2019-09-03 15:34:50
【问题描述】:

听说,在 Fortran77 中,函数中的所有局部变量都是在主程序执行开始时创建并存在于整个运行时,而不是在进入函数时创建并在退出时销毁。我不知道这在较新的 Fortran 中是否仍然适用。有什么方法可以测试出来吗? 一项可能有帮助的测试是检查变量是否在调用之间保持其值。这是一个简单的测试:

  program main
  call p()
  call p()
  call p()
 end program main

 subroutine p()
  real :: a(3)
  a=a+1
  write(*,*) a(1), a(2), a(3)
 end subroutine p

我使用gfortran 的测试表明数组a 在调用之间保留其值,与使用save 属性的行为相同。 我想知道这是 Fortran 语言中的标准还是取决于编译器实现。

【问题讨论】:

    标签: fortran fortran90


    【解决方案1】:

    为了好玩,我们可以尝试一个程序,在连续调用p() 之间可以调用一些其他例程(例如foo()):

    program main
        call p()
        !  call foo()  ! (*)
        call p()
        !  call foo()  ! (*)
        call p()
    end
    
    subroutine p()
        real :: a(3)
        a = a + 1
        write(*,*) "a = ", a
    end
    
    subroutine foo()
        real :: b(3)
        b = b * 10
        write(*,*) "b = ", b
    end
    

    加上注释的 (*) 行,我们得到

     ! gfortran-8.2
     a =    1.00000000       4.74066630E+21   1.00000000    
     a =    2.00000000       4.74066630E+21   2.00000000    
     a =    3.00000000       4.74066630E+21   3.00000000
    
     ! PGI18.10
     a =     1.000000        1.000000        1.000000    
     a =     2.000000        2.000000        2.000000    
     a =     3.000000        3.000000        3.000000    
    

    当 (*) 行未注释时,我们得到

     ! gfortran-8.2
     a =    1.00000000       4.74066630E+21   1.00000000    
     b =    10.0000000       4.74066641E+22   10.0000000    
     a =    11.0000000       4.74066641E+22   11.0000000    
     b =    110.000000       4.74066623E+23   110.000000    
     a =    111.000000       4.74066623E+23   111.000000
    
     ! PGI18.10
     a =     1.000000        1.000000        1.000000    
     b =     0.000000        0.000000        0.000000    
     a =     2.000000        2.000000        2.000000    
     b =     0.000000        0.000000        0.000000    
     a =     3.000000        3.000000        3.000000  
    

    (这只是局部变量行为的实验/说明(即不一定是“SAVE-ed”,因为它可能出现在更简单的情况下),请参阅the other answer and comments 了解详细说明。)

    【讨论】:

    • 当这是公认的答案时,为了将来的访问者的利益,您至少可以将实际答案添加到问题中吗?只需评论这些值已更改即可。
    • @VladimirF 嗨,我已经添加了指向您答案的指针(我也认为应该“接受”)。确实,我的只是为了好玩.. :) (尝试其他编译器可能也很有趣,但不幸的是我无法访问 atm。)
    【解决方案2】:

    这样的测试不能证明任何事情。在两次函数调用之间,堆栈中仍然存在一些垃圾这一事实可能纯属巧合。

    局部函数变量仅在定义其值的函数调用期间有效。在 Fortran 77 中也是如此。如果要保留该值,则必须将变量声明为 SAVE

    【讨论】:

    • 如果测试表明一个非常大的数组的每个元素在两次函数调用之间都保留了它们的值,那么该测试将证明一些事情,即本地数组的生命周期是整个执行而不是限制在函数调用。
    • 我说的是生命周期,但是你回复的第二句话说的是范围。
    • @YoujunHu 你完全错了,如果测试表明元素保留了它们的价值,那证明绝对没有。那只是undefined behaviour,数据只是作为无效垃圾保留在那里,即使它留在内存中也不允许您读取它。你很幸运,数组恰好是静态的,或者堆栈恰好在每次调用时布局相同。
    • @YoujunHu 你的其他评论也是错误的。关于SAVE 的评论是关于生命周期的,而不是关于范围的。只有使用SAVE,即使超出范围,数组仍然有效。没有save,一旦过程返回,该值是未定义的。
    • Fortran 77 不需要支持递归,隐式 SAVE 相当普遍。如果你想知道数据是否在超出范围时被覆盖,至少你必须调用另一个可能将数据存储在同一地址空间的过程,然后再查看数据是否被保留。
    猜你喜欢
    • 2011-09-12
    • 1970-01-01
    • 2013-07-27
    • 2011-04-18
    • 1970-01-01
    • 2011-07-15
    • 2010-09-19
    相关资源
    最近更新 更多