[Fortranのはじめ方] Part19: ALLOCATABLE配列と動的メモリ

Fortran

実行時にサイズが決まる配列を扱おう!

これまでのステップでは、プログラムの実行前にサイズが決まっている「静的配列」を扱ってきました。しかし、実際の科学技術計算では、ファイルから読み込むデータの量や計算の途中結果によって、必要な配列のサイズが変わることがよくあります🤔。

そんな時に活躍するのが ALLOCATABLE配列 です!🎉 ALLOCATABLE配列を使うと、プログラムの実行中に必要なメモリ量を決定し、そのサイズに合わせて配列のメモリ領域を確保(動的メモリ確保)することができます。これにより、メモリを無駄なく効率的に利用でき、より柔軟なプログラミングが可能になります。

このセクションでは、ALLOCATABLE配列の宣言方法、メモリの確保と解放、そしてその利点と注意点について学んでいきましょう。

ALLOCATABLE配列を宣言するには、通常の配列宣言に ALLOCATABLE 属性を追加します。この際、配列の次元数(ランク)はコロン : を使って指定しますが、各次元の具体的なサイズは指定しません。サイズは後でメモリを確保するときに決定します。

宣言の基本的な形式は以下の通りです。

型名, ALLOCATABLE :: 配列名(:, :, ...)

または

型名, ALLOCATABLE, DIMENSION(:, :, ...) :: 配列名

: の数が配列の次元数を表します。例えば、1次元配列なら (:)、2次元配列なら (:,:) となります。

【コード例】

PROGRAM allocatable_declare
  IMPLICIT NONE

  ! 1次元実数型のALLOCATABLE配列を宣言
  REAL, ALLOCATABLE :: vector(:)

  ! 2次元整数型のALLOCATABLE配列を宣言
  INTEGER, ALLOCATABLE, DIMENSION(:, :) :: matrix

  ! --- ここではまだメモリは確保されていない ---

END PROGRAM allocatable_declare

この時点では、配列 vectormatrix は存在しますが、まだ具体的な大きさを持たず、メモリ上の領域も確保されていません。メモリが割り当てられていない状態では、配列要素にアクセスすることはできません🚫。

2. メモリの確保 (ALLOCATE文)

ALLOCATABLE配列を使う準備が整ったら、いよいよメモリを確保します。メモリの確保には ALLOCATE 文を使用します。ALLOCATE 文で配列の具体的なサイズ(各次元の要素数)を指定します。

ALLOCATE 文の基本的な形式は以下の通りです。

ALLOCATE(配列名(下限1:上限1, 下限2:上限2, ...), STAT=ステータス変数)

各次元のサイズは 下限:上限 の形式で指定します。下限を省略した場合は、デフォルトで 1 となります(例: (10)(1:10) と同じ)。

STAT=ステータス変数 は非常に重要です!🚨 これはメモリ確保の結果を格納するための整数型変数です。

  • メモリ確保に成功した場合:ステータス変数に 0 が代入されます。✅
  • メモリ確保に失敗した場合(メモリ不足など):ステータス変数に 0以外(正の整数) が代入され、プログラムは停止しません。❌

STAT= を指定しない場合、メモリ確保に失敗するとプログラムがエラーで強制終了してしまいます。そのため、必ず STAT= を指定し、確保が成功したかどうかを確認する習慣をつけましょう。

【コード例】

PROGRAM allocate_example
  IMPLICIT NONE

  REAL, ALLOCATABLE :: vector(:)
  INTEGER, ALLOCATABLE :: matrix(:, :)
  INTEGER :: n, m, alloc_stat
  INTEGER :: i, j

  ! ユーザーに入力してもらうなどして、配列のサイズを決定
  WRITE(*,*) 'Enter the size for the vector:'
  READ(*,*) n
  WRITE(*,*) 'Enter the dimensions for the matrix (rows cols):'
  READ(*,*) m, n ! matrix は m行n列とする

  ! --- メモリの確保 ---
  WRITE(*,*) 'Allocating memory...'
  ALLOCATE(vector(n), STAT=alloc_stat)

  ! 確保の成否を確認
  IF (alloc_stat /= 0) THEN
    WRITE(*,*) 'Error: Failed to allocate memory for vector. Status:', alloc_stat
    STOP 'Allocation failed'
  ELSE
    WRITE(*,*) 'Successfully allocated vector of size', n
  END IF

  ! 2次元配列のメモリ確保(下限を0からにしてみる)
  ALLOCATE(matrix(0:m-1, 1:n), STAT=alloc_stat)
  IF (alloc_stat /= 0) THEN
    WRITE(*,*) 'Error: Failed to allocate memory for matrix. Status:', alloc_stat
    STOP 'Allocation failed'
  ELSE
    WRITE(*,*) 'Successfully allocated matrix of size', m, 'x', n
  END IF

  ! --- メモリ確保後、配列を使用できる ---
  WRITE(*,*) 'Initializing arrays...'
  vector = 1.0 ! 配列全体に値を代入
  DO i = 0, m-1
    DO j = 1, n
      matrix(i, j) = i * n + j
    END DO
  END DO

  WRITE(*,*) 'Vector(1):', vector(1)
  WRITE(*,*) 'Matrix(0,1):', matrix(0, 1)

  ! --- 使い終わったらメモリを解放する (次のセクションで解説) ---
  ! DEALLOCATE(vector, matrix)

END PROGRAM allocate_example

メモリが確保されているかどうかを確認するには、組み込み関数 ALLOCATED() を使うこともできます。これは論理値 (.TRUE. または .FALSE.) を返します。

IF (ALLOCATED(vector)) THEN
  WRITE(*,*) 'Vector is currently allocated.'
ELSE
  WRITE(*,*) 'Vector is not allocated.'
END IF

ALLOCATE する前に ALLOCATED() で確認し、もし既に割り当てられていたらエラーにする、といった使い方ができます。

注意: 一度 ALLOCATE した配列を、DEALLOCATE せずに再度 ALLOCATE しようとするとエラーになります。

3. メモリの解放 (DEALLOCATE文)

ALLOCATE 文で確保したメモリは、使い終わったら必ず解放する必要があります。これを怠ると、使われないメモリが確保されたまま残り続け、プログラム全体で利用できるメモリがどんどん減っていってしまいます(これをメモリリーク 💧 と呼びます)。メモリリークは、特に長時間実行するプログラムや、繰り返しメモリ確保を行うプログラムでは深刻な問題を引き起こす可能性があります。

メモリの解放には DEALLOCATE 文を使用します。

DEALLOCATE(配列名1, 配列名2, ..., STAT=ステータス変数)

ALLOCATE 文と同様に、STAT=ステータス変数 を指定して解放が成功したかを確認することが推奨されます。解放に成功すると 0 が、失敗すると 0 以外の値が代入されます。

【コード例】

PROGRAM deallocate_example
  IMPLICIT NONE

  REAL, ALLOCATABLE :: data_array(:)
  INTEGER :: n, alloc_stat, dealloc_stat

  n = 1000000 ! 大きな配列を確保してみる

  ! メモリ確保
  ALLOCATE(data_array(n), STAT=alloc_stat)
  IF (alloc_stat /= 0) THEN
    WRITE(*,*) 'Allocation failed!'
    STOP
  END IF
  WRITE(*,*) 'Memory allocated for', n, 'elements.'

  ! ... 配列を使った処理 ...
  data_array = 0.0
  WRITE(*,*) 'Array processing finished.'

  ! --- メモリの解放 ---
  WRITE(*,*) 'Deallocating memory...'
  DEALLOCATE(data_array, STAT=dealloc_stat)

  ! 解放の成否を確認
  IF (dealloc_stat /= 0) THEN
    WRITE(*,*) 'Error: Failed to deallocate memory. Status:', dealloc_stat
    ! 解放失敗は通常あまり起こらないが、念のためチェック
  ELSE
    WRITE(*,*) 'Memory successfully deallocated.'
  END IF

  ! 解放後、配列は使えなくなる
  ! WRITE(*,*) data_array(1) ! これはエラーになる!

  ! 解放されたか確認
  IF (.NOT. ALLOCATED(data_array)) THEN
     WRITE(*,*) 'data_array is now deallocated.'
  END IF

END PROGRAM deallocate_example
補足: Fortran 95 以降の規格では、サブルーチンや関数内で宣言された ALLOCATABLE 配列(SAVE属性が付いていないもの)は、その手続きから抜ける際に自動的に解放されるようになりました。しかし、明示的に DEALLOCATE する習慣をつけておく方が、メモリ管理を意識しやすくなり、より安全なプログラムにつながります。特に、モジュール変数やSAVE属性付きの変数としてALLOCATABLE配列を使う場合は、自動解放されないため明示的な DEALLOCATE が必要です。
注意: DEALLOCATE する配列は、必ず事前に ALLOCATE されている必要があります。ALLOCATE されていない配列や、既に DEALLOCATE された配列を再度 DEALLOCATE しようとするとエラーになります。ALLOCATED() 関数を使って事前に状態を確認すると安全です。

4. ALLOCATABLE配列の利点と活用

ALLOCATABLE配列を使うことには、主に以下の利点があります。

  • メモリ効率の向上 💰: プログラム実行時に本当に必要な分だけメモリを確保するため、静的配列のように予め最大サイズを見積もって無駄に大きな領域を確保する必要がありません。
  • プログラムの柔軟性向上 🤸: 実行時の状況に応じて配列サイズを変えられるため、様々な入力データに対応できる汎用的なプログラムを作成しやすくなります。
  • 大規模データ処理への対応 📈: 静的配列ではメモリ確保が難しいような非常に大きな配列も、必要なタイミングで確保・解放することで扱える可能性があります。(ただし、利用可能な物理メモリやOSの制限は受けます)

具体的な活用例としては、以下のような場面が考えられます。

  • ファイルからデータを読み込む際、ファイル内のデータ数を最初に読み取ってから、その数に合わせて配列を確保する。
  • 計算の反復回数や精度に応じて、一時的に必要となる作業用配列のサイズを決定する。
  • サブルーチンや関数の中で、入力引数に基づいて内部的な作業配列を確保する。

5. まとめと注意点

今回は、モダンFortranの重要な機能である ALLOCATABLE配列と動的メモリ確保について学びました。

  • ALLOCATABLE配列は ALLOCATABLE 属性をつけて宣言します(サイズは未定)。
  • メモリの確保は ALLOCATE 文で行い、サイズを指定します。STAT= で成功を確認しましょう。
  • 使い終わったメモリは DEALLOCATE 文で解放します。メモリリークを防ぎましょう。STAT= で成功を確認しましょう。
  • 組み込み関数 ALLOCATED() で、配列が現在割り当てられているかを確認できます。

ALLOCATABLE配列を使いこなすことで、メモリ効率が良く、柔軟性の高いプログラムを作成できるようになります✨。

【重要な注意点】

  • ALLOCATE されていない配列の要素にアクセスしようとすると、実行時エラーが発生します。
  • DEALLOCATE し忘れるとメモリリークの原因となります。
  • 既に ALLOCATE されている配列を再度 ALLOCATE しようとしたり、ALLOCATE されていない配列を DEALLOCATE しようとするとエラーになります。
  • ALLOCATEDEALLOCATE の際には、STAT= 指定子を使ってエラーハンドリングを行うことが強く推奨されます。

次のステップでは、サブルーチンや関数に引数を渡す際の INTENT 属性など、さらにモダンFortranの機能について学んでいきましょう!🚀

参考情報

コメント

タイトルとURLをコピーしました