12

经过许多痛苦的调试,我相信我已经找到了 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.)将保持原样,尽管类型声明语句中对negativeto进行了初始化。.FALSE.

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

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

为了可移植性和代码可重用性,语言(或编译器可能)不应该在每次调用子例程或函数时重新初始化所有内部变量吗?

4

3 回答 3

17

回答您的问题:是的,Fortran 确实通过函数和子例程调用保留了内部变量的值

在特定条件下...

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

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

这是(一些)语言新手感到惊讶和抱怨的原因。但无论他们多么努力地抱怨它都不会改变,所以你只需要(a)了解它并(b)在意识到它的情况下进行编程。

于 2010-08-18T08:05:16.920 回答
4

static这与 C 或 C++ 中的函数范围变量没有太大区别。

编程语言设计还处于起步阶段,早在FORTRAN开发之时。如果它今天是从头开始设计的,毫无疑问,许多设计决策都会有所不同。

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

到目前为止,存在大量遗留FORTRAN代码,编译器编写者不遗余力地保留向后兼容的语义。我不能引用标准中规定您所指出的行为的章节和经文,也不能引用其基本原理,但在这种情况下,向后兼容性胜过现代语言设计敏感性似乎是合理的。

于 2010-08-18T06:10:53.537 回答
4

这已经在这里讨论过好几次了,最近一次是在Fortran assignment on declaration and SAVE attribute gotcha

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

不同的语言是不同的,有不同的行为。

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

于 2010-08-18T09:00:37.213 回答