実行時にサイズが決まる配列を扱おう!
これまでのステップでは、プログラムの実行前にサイズが決まっている「静的配列」を扱ってきました。しかし、実際の科学技術計算では、ファイルから読み込むデータの量や計算の途中結果によって、必要な配列のサイズが変わることがよくあります🤔。
そんな時に活躍するのが ALLOCATABLE配列 です!🎉 ALLOCATABLE配列を使うと、プログラムの実行中に必要なメモリ量を決定し、そのサイズに合わせて配列のメモリ領域を確保(動的メモリ確保)することができます。これにより、メモリを無駄なく効率的に利用でき、より柔軟なプログラミングが可能になります。
このセクションでは、ALLOCATABLE配列の宣言方法、メモリの確保と解放、そしてその利点と注意点について学んでいきましょう。
1. 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
この時点では、配列 vector
と matrix
は存在しますが、まだ具体的な大きさを持たず、メモリ上の領域も確保されていません。メモリが割り当てられていない状態では、配列要素にアクセスすることはできません🚫。
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
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
しようとするとエラーになります。 ALLOCATE
やDEALLOCATE
の際には、STAT=
指定子を使ってエラーハンドリングを行うことが強く推奨されます。
次のステップでは、サブルーチンや関数に引数を渡す際の INTENT
属性など、さらにモダンFortranの機能について学んでいきましょう!🚀
参考情報
- Fortran 入門: 配列 – NAG: 配列の基本的な使い方から動的割り付けまで解説されています。
- Fortranにおける配列の宣言方法と関連機能 – Qiita: 配列宣言、動的割り付け、仮引数について詳しく解説されています。
- 割り振り可能配列 – IBM Documentation: IBMコンパイラのドキュメントですが、ALLOCATABLE配列の概要が説明されています。
コメント