Fortran是否通过函数和子例程调用保留内部变量的值?

时间:2010-08-18 05:30:19

标签: function fortran scope subroutine

经过多次痛苦的调试后,我相信我已经找到了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.

如您所见,似乎一旦调用了函数或子例程,逻辑变量negative(如果切换到.TRUE.)仍然如此,尽管negative已初始化到类型声明语句中的.FALSE.

当然,我可以通过添加一行来纠正这个问题    负= =。 在我的函数和子例程中声明变量之后。

然而,这对我来说似乎很奇怪,这是必要的。

为了便携性和代码可重用性,每次调用子例程或函数时,语言(或编译器)是否都不需要重新初始化所有内部变量?

3 个答案:

答案 0 :(得分:13)

回答你的问题:是的Fortran确实通过函数和子程序调用来保留内部变量的值

在某些条件下......

如果使用SAVE属性声明内部变量,则将其值从一次调用保存到下一次。当然,这在某些情况下很有用。

但是,您的问题是在首次了解Fortran的一个陷阱时的常见反应:如果您在其声明中初始化内部变量,那么它会自动获取SAVE属性。您已在子例程中完成了这一操作。这符合标准。如果您不希望发生这种情况,请不要在声明中初始化。

这引起了(一些)新语言者的惊讶和抱怨。但无论他们多么努力地抱怨它都不会改变所以你只需要(a)了解它并且(b)计划对它的认识。

答案 1 :(得分:4)

这与C或C ++中的static函数范围变量没有什么不同。

编程语言设计还处于起步阶段,当FORTRAN出现时 发达。如果今天从头开始设计,毫无疑问很多设计 决定会有所不同。

最初,FORTRAN甚至不支持递归,没有动态内存 分配,程序使用COMMON块玩各种类型的双打游戏 和EQUIVALENCE语句,程序可以有多个入口点......所以 内存模型基本上是为编译器/链接器布局所有内容,甚至是本地的 变量和数字常量,进入固定存储位置,而不是 堆栈。如果您愿意,您甚至可以编写将“2”的值更改为的代码 “42”!

到目前为止,存在大量遗留FORTRAN代码,编译器编写者竭尽全力保留向后兼容的语义。我不能引用要求你所记录的行为的标准的章节和经文,也不能引用它的基本原理,但在这种情况下,向后兼容性胜过现代语言设计的敏感性似乎是合理的。

答案 2 :(得分:3)

此处已多次讨论,最近一次是在Fortran assignment on declaration and SAVE attribute gotcha

您不必通过实验发现这种行为,而是在更好的教科书中明确说明。

不同的语言不同,行为也不同。

这种行为有历史原因。 Fortran 77及更早版本的许多编译器在程序调用中保留了所有局部变量的值。程序员不应该依赖这种行为,但很多人都这样做。根据标准,如果您希望局部变量(非COMMON)保留其值,则需要使用“SAVE”。但许多程序员忽略了这一点。在那个时代,程序不经常移植到不同的平台和编译器,因此可能永远不会注意到错误的假设。在遗留程序中发现此问题是很常见的 - 当前的Fortran编译器通常提供编译器开关以使所有变量都被保存。语言标准要求所有局部变量保留其值是愚蠢的。但是,一个中间要求可以拯救许多不小心使用“SAVE”的程序,那就是要求在其声明中初始化的所有变量自动拥有SAVE属性。因此你发现了......