我已经阅读save
了(英特尔的)语言参考文档中的声明,但我不能完全理解它的作用。有人可以用简单的语言向我解释当save
语句包含在模块中时的含义吗?
3 回答
原则上,当模块超出范围时,该模块的变量将变为未定义的——除非它们使用 SAVE 属性声明,或者使用了 SAVE 语句。“未定义”意味着如果您再次使用该模块,则不允许您依赖具有先前值的变量-当您重新访问该模块时它可能具有先前的值,或者它可能没有-无法保证. 但是许多编译器不会对模块变量执行此操作——变量可能保留它们的值——编译器不值得努力确定模块是否仍在范围内,并且可能模块变量被视为全局变量变量——但不要依赖它!为了安全起见,请使用“保存”或“使用”
“保存”在过程中也很重要,用于在子例程或函数的调用(由@ire_and_curses 编写)之间存储“状态”——“第一次调用”初始化、计数器等。
subroutine my_sub (y)
integer :: var
integer, save :: counter = 0
logical, save :: FirstCall = .TRUE.
counter = counter + 1
write (*, *) counter
if (FirstCall) then
FirstCall = .FALSE.
....
end if
var = ....
等等
在此代码片段中,“计数器”将报告子例程 x 的调用次数。虽然实际上在 Fortran >=90 中可以省略“保存”,因为声明中的初始化意味着“保存”。
与模块情况相反,在现代编译器中,没有保存属性或声明时初始化,过程的局部变量在调用过程中丢失其值是正常的。因此,如果您尝试在稍后的调用中使用“var”,然后在该调用中重新定义它,则该值是未定义的,并且可能不是在先前调用该过程时计算的值。
这与许多 FORTRAN 77 编译器的行为不同,其中一些编译器保留了所有局部变量的值,尽管语言标准并不要求这样做。一些旧程序是根据这种非标准行为编写的——这些程序在较新的编译器上会失败。许多编译器可以选择使用非标准行为并“保存”所有局部变量。
稍后编辑:使用代码示例更新,该示例显示不正确使用应具有 save 属性但没有的局部变量:
module subs
contains
subroutine asub (i, control)
implicit none
integer, intent (in) :: i
logical, intent (in) :: control
integer, save :: j = 0
integer :: k
j = j + i
if ( control ) k = 0
k = k + i
write (*, *) 'i, j, k=', i, j, k
end subroutine asub
end module subs
program test_saves
use subs
implicit none
call asub ( 3, .TRUE. )
call asub ( 4, .FALSE. )
end program test_saves
子程序的局部变量k被故意误用——在这个程序中,它在第一次调用时被初始化,因为control是 TRUE,但在第二次调用时control是 FALSE,所以k没有被重新定义。但是没有保存属性k是未定义的,所以使用它的值是非法的。
用gfortran编译程序,我发现k无论如何都保留了它的值:
i, j, k= 3 3 3
i, j, k= 4 7 7
使用 ifort 和激进的优化选项编译程序,k 失去了它的价值:
i, j, k= 3 3 3
i, j, k= 4 7 4
使用带有调试选项的 ifort,在运行时检测到问题!
i, j, k= 3 3 3
forrtl: severe (193): Run-Time Check Failure. The variable 'subs_mp_asub_$K' is being used without being defined
通常,一旦执行离开当前过程,局部变量就会超出范围,因此在以前的调用中它们的值没有“记忆”。SAVE
是一种指定过程中的变量应从一次调用到下一次调用保持其值的方法。当您想在过程中存储状态时,它很有用,例如保持运行总计或维护变量的配置。
这里有一个很好的解释,有一个例子。
一个简短的解释可能是:该属性save
表示必须在对同一子例程/函数的不同调用中保留变量的值。否则,通常当您从子例程/函数返回时,“局部”变量会丢失它们的值,因为存储这些变量的内存被释放。就像static
在 C 中一样,如果你知道这种语言的话。