3

配列の要素を参照するためのインデックスが実際に想定範囲外である場合、Fortranがこの状況をどのように処理するか混乱しています。

問題を説明するための簡単なコードを次に示します。

PROGRAM test_matrix_out

USE mod_writearray

IMPLICIT NONE
INTEGER :: i,j,m,n
REAL    :: k
REAL, Dimension(:,:),ALLOCATABLE :: A

m = 3
n = 4
ALLOCATE(A(m,n))

k = 1

DO i=1,m
    DO j=1,n
        A(i,j)=k
        k=k+1
    ENDDO
ENDDO

CALL writearray(A)
WRITE(*,*)
WRITE(*,*) A(1,:)
WRITE(*,*)
WRITE(*,*) A(2,:)
WRITE(*,*)
WRITE(*,*) A(0,:)
WRITE(*,*)
WRITE(*,*) A(4,:)
WRITE(*,*)
WRITE(*,*) A(5,:)
WRITE(*,*)
WRITE(*,*) A(100,:)
WRITE(*,*)
WRITE(*,*) A(:,1)
WRITE(*,*)
WRITE(*,*) A(:,2)
WRITE(*,*)
WRITE(*,*) A(:,0)
WRITE(*,*)
WRITE(*,*) A(:,4)
WRITE(*,*)
WRITE(*,*) A(:,5)
WRITE(*,*)
WRITE(*,*) A(:,100)


DEALLOCATE(A)

END PROGRAM test_matrix_out

次の結果が得られます。

   1.000000       2.000000       3.000000       4.000000

   5.000000       6.000000       7.000000       8.000000

  0.0000000E+00   9.000000       10.00000       11.00000

   2.000000       3.000000       4.000000      0.0000000E+00

   6.000000       7.000000       8.000000      0.0000000E+00

  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00

   1.000000       5.000000       9.000000

   2.000000       6.000000       10.00000

 -1.0097448E-28  8.9776148E-39  0.0000000E+00

   4.000000       8.000000       12.00000

  0.0000000E+00  0.0000000E+00  0.0000000E+00

 -3.3631163E-44  1.4293244E-43  0.0000000E+00

なぜこれが起こるのですか?

4

2 に答える 2

7

A(i、j)と書くと、コンパイラはそのメモリ位置のアドレスを計算します。たとえば、http://en.wikipedia.org/wiki/Array_data_structure#Multidimension_arraysを参照してください。コンパイラは通常、これが言語の規則に従って正当なアドレスであるかどうかを判断しません。ディメンションを超えてインデックスを使用することは違法です。これを行わないのはプログラマーの責任です。Fortranの利点の1つは、この間違いに対する実行時チェックを追加できることです。従来の伝承では、実行時の添え字チェックは高価ですが、テストしたところ、実行時のコストはごくわずかであり、プログラムの製品版ではそのままにしておくことがよくあります。

メモリを読み取っている場合、インデックスの間違いの結果として、メモリの場所がプログラムに属するメモリの外にあるほど離れていない限り、間違った値を取得する可能性があります。これにより、障害が発生します。メモリに書き込んでいる場合、配列の他の場所、他の変数に属している、またはプログラムの内部データ構造に属しているメモリが破損します。割り当て解除の欠如が原因でどのような問題が発生する可能性があるかを確認してください。インデックスエラーがプログラムの実行時の問題を引き起こした質問の例。

于 2012-07-19T06:54:25.530 に答える
5

あなたが見ているのは、プログラムのコンパイルに使用するコンパイラが、実行時に配列が範囲外になるかどうかをチェックしていないということです。したがって、コンパイラとマシンによっては、何でもかまいません。場合によっては、メモリに明示的に割り当てられていない配列要素を参照できることがあります。これは、例で発生することです。この場合、範囲外の要素の値は、プログラムの実行時にそのメモリアドレスにある値になります。要求されたメモリアドレスが存在しないか、アクセスできない場合、プログラムはセグメンテーション違反で失敗します。

私は現在のFortran標準のドラフトを見ていましたが、範囲外の配列要素にアクセスすることが定義された動作であるかどうかについてのステートメントを見つけることができませんでした。これらの問題を回避するには、-C(境界チェック)フラグを使用してプログラムをコンパイルします。可能であれば、プログラムは、どの配列のどの要素が範囲外になったのかを通知します。開発中に使用-Cしますが、コードの速度が大幅に低下するため、本番環境では使用しません。

また、将来の参考のために、この種の質問をするとき(たとえば、私のプログラムがこれを出力する理由)、使用されているコンパイラ(バージョン番号付き)とターゲットアーキテクチャに関する情報を含めるのが最善です。

于 2012-07-19T04:42:15.427 に答える