代码之家  ›  专栏  ›  技术社区  ›  EMiller

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

  •  8
  • EMiller  · 技术社区  · 14 年前

    经过非常痛苦的调试,我相信我已经找到了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. ,尽管初始化了 消极的 .FALSE. 在类型声明语句中。

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

    然而,我觉得很奇怪,这是必要的。

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

    3 回复  |  直到 14 年前
        1
  •  13
  •   High Performance Mark    14 年前

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

    在某些情况下…

    如果用save属性声明一个内部变量,它的值将从一个调用保存到下一个调用。当然,这在某些情况下是有用的。

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

    这就是(一些)新来者对该语言感到惊讶和抱怨的原因。但是,不管他们多么抱怨,它不会改变,所以你只需要(a)了解它,(b)了解它的计划。

        2
  •  4
  •   Jim Lewis    14 年前

    这和 static 函数在C或C++中的作用域变量。

    编程语言设计在其幼年时期,早在 FORTRAN 是 发达的。如果今天它是从头开始设计的,毫无疑问,许多设计 决定会有所不同。

    原来, 福特朗 甚至不支持递归,没有动态内存 分配,程序玩各种类型的双关游戏 COMMON 阻碍 和 EQUIVALENCE 语句、过程可以有多个入口点……因此 内存模型基本上是为了让编译器/链接器布局所有内容,即使是本地的 变量和数字文本常量,放入固定的存储位置,而不是打开 堆栈。如果你愿意,你甚至可以写一些代码,把“2”的值改为 “42”!

    到目前为止,有很多遗产 福特朗 代码就在那里,编译器编写者为了保持向后兼容的语义而付出了巨大的努力。我不能引用你提到的规范中的章节,也不能引用它的基本原理,但在这种情况下,向后兼容性胜过现代语言设计的敏感性似乎是合理的。

        3
  •  3
  •   Community CDub    8 年前

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

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

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

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