Fortran 是否通过函数和子例程调用保留内部变量的值?
经过多次痛苦的调试后,我相信我已经找到了 Fortran 的一个独特属性,我想在 stackoverflow 上验证它。
我注意到的是,至少内部逻辑变量的值在函数或子例程调用中得以保留。
下面是一些示例代码来说明我的观点:
PROGRAM function_variable_preserve
IMPLICIT NONE
CHARACTER(len=8) :: func_negative_or_not ! Declares function name
INTEGER :: input
CHARACTER(len=8) :: output
input = -9
output = func_negative_or_not(input)
WRITE(*,10) input, " is ", output
10 FORMAT("FUNCTION: ", I2, 2A)
CALL sub_negative_or_not(input, output)
WRITE(*,20) input, " is ", output
20 FORMAT("SUBROUTINE: ", I2, 2A)
WRITE(*,*) 'Expected negative.'
input = 7
output = func_negative_or_not(output)
WRITE(*,10) input, " is ", output
CALL sub_negative_or_not(input, output)
WRITE(*,20) input, " is ", output
WRITE(*,*) 'Expected positive.'
END PROGRAM function_variable_preserve
CHARACTER(len=*) FUNCTION func_negative_or_not(input)
IMPLICIT NONE
INTEGER, INTENT(IN) :: input
LOGICAL :: negative = .FALSE.
IF (input < 0) THEN
negative = .TRUE.
END IF
IF (negative) THEN
func_negative_or_not = 'negative'
ELSE
func_negative_or_not = 'positive'
END IF
END FUNCTION func_negative_or_not
SUBROUTINE sub_negative_or_not(input, output)
IMPLICIT NONE
INTEGER, INTENT(IN) :: input
CHARACTER(len=*), INTENT(OUT) :: output
LOGICAL :: negative = .FALSE.
IF (input < 0) THEN
negative = .TRUE.
END IF
IF (negative) THEN
output = 'negative'
ELSE
output = 'positive'
END IF
END SUBROUTINE sub_negative_or_not
这是输出:
FUNCTION: -9 is negative
SUBROUTINE: -9 is negative
Expected negative.
FUNCTION: 7 is negative
SUBROUTINE: 7 is negative
Expected positive.
如您所见,一旦函数或子例程被调用一次,逻辑变量负
,如果切换到尽管在类型声明语句中将
仍然如此。否定
初始化为.FALSE.
,.TRUE.
当然,我可以通过添加一行来纠正这个问题 负数 = .FALSE。 在我的函数和子例程中声明变量之后。
然而,这对我来说似乎很奇怪,这是必要的。
为了可移植性和代码可重用性,语言(或编译器可能)不应该要求每次调用子例程或函数时重新初始化所有内部变量吗?
After much painful debugging, I believe I've found a unique property of Fortran that I'd like to verify here at stackoverflow.
What I've been noticing is that, at the very least, the value of internal logical variables are preserved across function or subroutine calls.
Here is some example code to illustrate my point:
PROGRAM function_variable_preserve
IMPLICIT NONE
CHARACTER(len=8) :: func_negative_or_not ! Declares function name
INTEGER :: input
CHARACTER(len=8) :: output
input = -9
output = func_negative_or_not(input)
WRITE(*,10) input, " is ", output
10 FORMAT("FUNCTION: ", I2, 2A)
CALL sub_negative_or_not(input, output)
WRITE(*,20) input, " is ", output
20 FORMAT("SUBROUTINE: ", I2, 2A)
WRITE(*,*) 'Expected negative.'
input = 7
output = func_negative_or_not(output)
WRITE(*,10) input, " is ", output
CALL sub_negative_or_not(input, output)
WRITE(*,20) input, " is ", output
WRITE(*,*) 'Expected positive.'
END PROGRAM function_variable_preserve
CHARACTER(len=*) FUNCTION func_negative_or_not(input)
IMPLICIT NONE
INTEGER, INTENT(IN) :: input
LOGICAL :: negative = .FALSE.
IF (input < 0) THEN
negative = .TRUE.
END IF
IF (negative) THEN
func_negative_or_not = 'negative'
ELSE
func_negative_or_not = 'positive'
END IF
END FUNCTION func_negative_or_not
SUBROUTINE sub_negative_or_not(input, output)
IMPLICIT NONE
INTEGER, INTENT(IN) :: input
CHARACTER(len=*), INTENT(OUT) :: output
LOGICAL :: negative = .FALSE.
IF (input < 0) THEN
negative = .TRUE.
END IF
IF (negative) THEN
output = 'negative'
ELSE
output = 'positive'
END IF
END SUBROUTINE sub_negative_or_not
This is the output:
FUNCTION: -9 is negative
SUBROUTINE: -9 is negative
Expected negative.
FUNCTION: 7 is negative
SUBROUTINE: 7 is negative
Expected positive.
As you can see, it appears that once the function or subroutine is called once, the logical variable negative
, if switched to .TRUE.
, remains as such despite the initialization of negative
to .FALSE.
in the type declaration statement.
I could, of course, correct this problem by just adding a line
negative = .FALSE.
after declaring the variable in my function and subroutine.
However, it seems very odd to me that this be necessary.
For the sake of portability and code reusability, shouldn't the language (or compiler maybe) require re-initialization of all internal variables each time the subroutine or function is called?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
回答你的问题:是的,Fortran 确实通过函数和子例程调用保留内部变量的值。
在某些条件下...
如果您使用 SAVE 属性声明一个内部变量,则它的值将从一次调用保存到下一次调用。当然,这在某些情况下很有用。
然而,您的问题是第一次了解 Fortran 陷阱之一时的常见反应:如果您在其声明中初始化内部变量,那么它会自动获取 SAVE 属性。您已经在子例程中完全做到了这一点。这是符合标准的。如果您不希望发生这种情况,请不要在声明中进行初始化。
这是(一些)该语言的新手感到惊讶和抱怨的原因。但无论他们多么努力地抱怨,它都不会改变,所以你只需要 (a) 了解它并 (b) 在意识到它的情况下进行编程。
To answer your question: Yes Fortran does preserve the value of internal variables through function and subroutine calls.
Under certain conditions ...
If you declare an internal variable with the SAVE attribute it's value is saved from one call to the next. This is, of course, useful in some cases.
However, your question is a common reaction upon first learning about one of Fortran's gotchas: if you initialise an internal variable in its declaration then it automatically acquires the SAVE attribute. You have done exactly that in your subroutines. This is standard-conforming. If you don't want this to happen don't initialise in the declaration.
This is the cause of much surprise and complaint from (some) newcomers to the language. But no matter how hard they complain it's not going to change so you just have to (a) know about it and (b) program in awareness of it.
这与 C 或 C++ 中的静态函数作用域变量没有太大区别。
当
FORTRAN
出现时,编程语言设计还处于起步阶段。发达。如果今天从头开始设计,毫无疑问许多设计
决策将会有所不同。
最初,
FORTRAN
甚至不支持递归,没有动态内存分配,程序用
COMMON
块玩各种类型双关游戏和 EQUIVALENCE 语句,过程可以有多个入口点......所以
内存模型基本上是让编译器/链接器布置所有内容,甚至是本地的
变量和数字常量放入固定的存储位置,而不是放在
堆栈。如果您愿意,您甚至可以编写代码将“2”的值更改为
“42”!
到目前为止,已经有大量的遗留
FORTRAN
代码,并且编译器编写者不遗余力地保留向后兼容的语义。我无法引用强制执行您所指出的行为的标准中的章节和诗句,也无法引用其基本原理,但在这种情况下,向后兼容性胜过现代语言设计敏感性似乎是合理的。This isn't too different from
static
function-scoped variables in C or C++.Programming language design was in its infancy, back when
FORTRAN
wasdeveloped. If it were being designed from scratch today, no doubt many of the design
decisions would have been different.
Originally,
FORTRAN
didn't even support recursion, there was no dynamic memoryallocation, programs played all sorts of type-punning games with
COMMON
blocksand
EQUIVALENCE
statements, procedures could have multiple entry points....so thememory model was basically for the compiler/linker to lay out everything, even local
variables and numeric literal constants, into fixed storage locations, rather than on
the stack. If you wanted, you could even write code that changed the value of "2" to
"42"!
By now, there is an awful lot of legacy
FORTRAN
code out there, and compiler writers go to great lengths to preserve backward-compatible semantics. I can't quote chapter and verse from the standard that mandates the behavior you've noted, nor its rationale, but it seems reasonable that backward compatibility trumped modern language design sensibilities, in this instance.这已经在这里讨论过几次,最近一次是在 Fortran 声明和赋值SAVE 属性陷阱
您不必通过实验来发现这种行为,更好的教科书中已经明确说明了这一点。
不同的语言是不同的,并且有不同的行为。
这种行为是有历史原因的。 Fortran 77 及更早版本的许多编译器在过程调用中保留所有局部变量的值。程序员不应该依赖这种行为,但很多人却这么做了。根据标准,如果您希望局部变量(非 COMMON)保留其值,则需要使用“SAVE”。但很多程序员忽略了这一点。在那个时代,程序很少被移植到不同的平台和编译器,因此错误的假设可能永远不会被注意到。在遗留程序中很容易发现这个问题——当前的 Fortran 编译器通常提供编译器开关来保存所有变量。对于语言标准来说,要求所有局部变量保留其值是愚蠢的。但是,可以挽救许多不小心使用“SAVE”的程序的中间要求是要求在其声明中初始化的所有变量自动具有 SAVE 属性。因此你发现了......
This has been discussed several times here, most recently at Fortran assignment on declaration and SAVE attribute gotcha
You don't have to discover this behavior by experimentation, it is clearly stated in the better textbooks.
Different languages are different and have different behaviors.
There is a historical reason for this behavior. Many compilers for Fortran 77 and earlier preserved the values of ALL local variables across calls of procedures. Programmers weren't supposed to rely upon this behavior but many did. According to the standard, if you wanted a local variable (non-COMMON) to retain its value you needed to use "SAVE". But many programmers ignored this. In that era programs were less frequently ported to different platforms and compilers, so incorrect assumptions might never be noticed. It is common to find this problem in legacy programs -- current Fortran compilers typically provide a compiler switch to cause all variables to be saved. It would be silly for the language standard to require that all local variables retain their values. But an intermediate requirement that would rescue many programs that were careless with "SAVE" would be to require all variables initialized in their declarations to automatically have the SAVE attribute. Hence what you discovered....