12

非常に骨の折れるデバッグの後、私はここでstackoverflowで確認したいFortranのユニークなプロパティを見つけたと思います。

私が気付いたのは、少なくとも、内部論理変数の値は、関数またはサブルーチンの呼び出し全体で保持されるということです。

これが私のポイントを説明するためのいくつかのサンプルコードです:

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.

ご覧のとおり、関数またはサブルーチンが1回呼び出されると、論理変数negativeは、に切り替えられた場合、型宣言ステートメントでto.TRUE.が初期化されても、そのまま残ります。negative.FALSE.

もちろん、negative = .FALSEの行を追加するだけで、この問題を修正できます。関数とサブルーチンで変数を宣言した後。

しかし、これが必要なのは私には非常に奇妙に思えます。

移植性とコードの再利用性のために、言語(またはコンパイラー)は、サブルーチンまたは関数が呼び出されるたびに、すべての内部変数の再初期化を要求するべきではありませんか?

4

3 に答える 3

17

あなたの質問に答えるには:はい、Fortran は関数とサブルーチンの呼び出しを通じて内部変数の値を保持します

特定の条件下で...

SAVE 属性を使用して内部変数を宣言すると、その値は次の呼び出しまで保存されます。もちろん、これは場合によっては便利です。

ただし、あなたの質問は、Fortran の落とし穴の 1 つについて最初に学んだときの一般的な反応です。宣言で内部変数を初期化すると、SAVE 属性が自動的に取得されます。サブルーチンでまさにそれを行いました。これは規格に準拠しています。これが発生したくない場合は、宣言で初期化しないでください。

これは、(一部の) 新参者から言語への多くの驚きと不満の原因です。しかし、彼らがどんなに文句を言っても、それは変わらないので、(a) それについて知り、(b) それを意識してプログラムする必要があります。

于 2010-08-18T08:05:16.920 に答える
4

これについては、ここで何度か議論されていますが、最近では、Fortran の宣言での代入と SAVE 属性の落とし穴で議論されています。

実験によってこの動作を発見する必要はありません。より優れた教科書に明確に記載されています。

言語が異なれば、動作も異なります。

この動作には歴史的な理由があります。Fortran 77 以前の多くのコンパイラは、プロシージャの呼び出し間ですべてのローカル変数の値を保持していました。プログラマーはこの動作に依存するべきではありませんでしたが、多くのプログラマーは依存していました。標準によると、ローカル変数 (非 COMMON) にその値を保持させたい場合は、"SAVE" を使用する必要がありました。しかし、多くのプログラマーはこれを無視しました。その時代には、プログラムがさまざまなプラットフォームやコンパイラに移植される頻度が低かったため、誤った仮定に気付かれることはありませんでした。この問題は、レガシー プログラムでよく見られます。現在の Fortran コンパイラでは、通常、すべての変数を保存するコンパイラ スイッチが提供されています。すべてのローカル変数がその値を保持することを言語標準で要求するのはばかげています。しかし、「SAVE」で不注意だった多くのプログラムを救済する中間要件は、宣言で初期化されたすべての変数が自動的に SAVE 属性を持つようにすることです。したがって、あなたが発見したもの....

于 2010-08-18T09:00:37.213 に答える
4

これはstatic、C や C++ の関数スコープの変数と大差ありません。

プログラミング言語の設計は、開発されたばかりの初期段階にありましたFORTRAN。今日ゼロから設計されていた場合、設計上の決定の多くが異なっていたことは間違いありません。

もともと、FORTRAN再帰さえサポートしていなかったし、動的メモリ割り当てもなかったし、プログラムはCOMMONブロックとEQUIVALENCEステートメントを使ってあらゆる種類の型遊びゲームをプレイしていたし、手続きは複数のエントリポイントを持つことができた....だからメモリモデルは基本的にコンパイラのためのものだった/リンカを使用して、ローカル変数や数値リテラル定数を含め、すべてをスタックではなく固定ストレージの場所にレイアウトします。必要に応じて、"2" の値を "42" に変更するコードを作成することもできます。

現在、非常に多くのレガシーFORTRANコードが存在しており、コンパイラの作成者は下位互換性のあるセマンティクスを維持するために多大な努力を払っています。あなたが指摘した動作を義務付ける標準から章と節を引用することはできません。

于 2010-08-18T06:10:53.537 に答える