经过多次痛苦的调试后,我相信我已经找到了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.
。
当然,我可以通过添加一行来纠正这个问题 负= =。 在我的函数和子例程中声明变量之后。
然而,这对我来说似乎很奇怪,这是必要的。
为了便携性和代码可重用性,每次调用子例程或函数时,语言(或编译器)是否都不需要重新初始化所有内部变量?
答案 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属性。因此你发现了......