FreeBASIC による
動的メモリ 管理(割り当て・解放)について
序文:
FreeBASIC では、3 種類の基本的なメモリ割り当てをサポートしています:
- 静的割り当ては、静的変数とグローバル変数に対して行われます:
メモリの割り当ては、プログラムの実行時に一度だけ行われ、プログラムの存続期間中は持続します。
- スタックの確保は、手続きのパラメータやローカル変数に対して行われます:
対応するブロックに入る時にメモリが確保され、ブロックから出る時に解放されることが、必要な回数繰り返されます。
- このページでは、動的割り当てについて説明します。
静的割り当てとスタック割り当てには、2つの共通点があります:
- 変数のサイズが、コンパイル時にわかっている。
- メモリの割り当てと解放は自動的に行われます(変数がインスタンス化されてから破棄されるとき)。
ユーザーはこのような変数の破棄を予測することはできません。
ほとんどの場合、これでいいと思います。しかし、これらの制約のどちらか一方が問題となる状況もあります(必要なメモリがユーザーの入力に依存する場合、サイジングは実行中にしか決められません)。
コンパイル時にすべてのサイズを宣言しなければならない場合、最良の方法は、必要な変数の最大サイズを推定して、それで足りることを願うことです。
しかし、これは、少なくとも 3つの理由から、悪い解決策です:
- 第一に、メモリが十分に使われなければ、無駄遣いにつながります。
- 第二に、ほとんどの通常の変数は、メモリ中のスタックと呼ばれる部分に割り当てられます。
プログラムのスタック・メモリの量は、通常、かなり少なく設定されています(デフォルトでは 1MBまたは 2MB)。
これを超えると、スタック・オーバーフローが発生し、プログラムが中断してしまいます。
- 第三に、最も重要なことですが、これは人為的な制限やオーバーフローにつながる可能性があります。
必要なメモリが予約されたメモリよりも大きくなった場合、どうなるでしょうか(プログラムの停止、ユーザーへのメッセージの表示、...)。
幸いなことに、これらの問題は、メモリの動的割り当てで簡単に解決できます。
動的メモリ割り当てとは、プログラムを実行する際に、必要に応じてオペレーティング・システムにメモリを要求する方法です。
このメモリは、プログラムの限られたスタック・メモリからではなく、ヒープと呼ばれるオペレーティング・システムが管理するはるかに大きなメモリ・プールから割り当てられます。
最近のマシンでは、ヒープの大きさは数ギガバイトにもなります。
動的メモリ割り当てに関するキーワード
動的なメモリの割り当て/解放のためのキーワードは 2種類あります:
同じメモリ・ブロックを管理する場合、この 2つのセットの間でキーワードを混在させることは強く推奨されません。
Allocate / Callocate / Reallocate / Deallocate を使った動的メモリ割り当て管理
各キーワードの詳細な構文、正確な説明、および例については、それぞれの文書ページを参照してください。
追加の機能と使用上のヒント:
- Allocate / Callocate / Reallocate では、メモリの割り当てが成功したかどうかを知ることができます(そうでない場合は null ポインタが返されます)。
- 割り当てられたメモリ・サイズが 0 バイトで要求された場合でも、NULL ではないポインタが返され、割り当てられた値を使って解除する必要があります(Deallocate と同様の動作をする Reallocate(pointer, 0) を除く)。
- メモリの解放については、Deallocate はどのようなタイプのポインターでも(いずれにせよ正しい値で)呼び出すことができます。
- ユーザーが、どうしてもオブジェクトにこのタイプの割り当てを使いたい場合(たとえば再割り当てができるようにするため)、必要に応じて構築子と解体子を(メンバー・アクセス演算子を使って)適切な方法で手動で呼び出す必要があります。
- デアロケート後、ポインタの値は無効になり(無効なメモリ・アドレスを指している)、再利用(再参照や Deallocate の再呼び出し)すると未定義の動作になります。
New / Delete を使った動的メモリ割り当て管理
各キーワードについては、個々の文書ページにある詳細な構文、正確な説明、および例を参照してください。
追加機能と使用上のヒント:
- 以前は、New がメモリの割り当てに成功してもシグナルが出ませんでした(プログラムがハングアップする)。
fbc rev 1.06 では、New が失敗した場合に NULL ポインタを返すことで問題を解決しました。
- 割り当てられたメモリ・サイズが 0 バイト(New predefined_datatype[0])を要求した場合でも、NULL ではないポインタが返され、その値を使って割り当てを解除する必要があります。
- オブジェクトの破棄やメモリの解放のためには、適切に型付けされたポインタに対して Delete が呼び出されなければなりません(そうしないと、オブジェクトの解体子が呼び出されなかったり、ミスコールされたりして、メモリリークや Delete[] によるクラッシュが発生する可能性があります)。
- ユーザーは、単純な定義済みの型(固定長文字列を除く)に対してもこのタイプの割り当てを使うことができますが、これは割り当てのより単純な使用構文を除いて、機能的には何も追加されません。
- 破壊+割り当て解除の後、ポインタの値は無効になり(無効なメモリアドレスを指している)、その再利用(再参照や Delete の再呼び出し)は未定義の動作となります。
- 使用する場合、特別な Placement New (既に割り当てられているメモリを使用)は、オブジェクトの構築のみを誘発するので、Delete の使用は禁止されています(割当解除の二重要求を避けるため)。
必要であれば、オブジェクトの唯一の破棄はユーザーが手動で行わなければなりません(メンバー・アクセス演算子を使って解体子を呼び出す)。
Redim / Erase を使ったバリアント
FreeBASIC は、動的配列(可変長配列)にも対応しています。
動的配列が要素を格納するために使うメモリは、実行時にヒープに割り当てられます。
動的配列には、複雑なオブジェクトだけでなく、単純な型も含めることができます。
Redim を使うと、ユーザーが構築子 / 解体子を呼び出す必要がありません。
Redim が、要素の追加 / 削除時に 自動的に行うためです。
Erase は、残りのすべての要素を破棄して、それらに割り当てられたメモリを完全に解放します。
4つの方法を比較した使用例
4つのメソッドを比較して、オブジェクトのセットでの使用例:
- 3 then 4 objects:
Callocate,
Reallocate,
Deallocate, (+ .constructor + .destructor).
- 3 objects:
New,
Delete.
- 3 objects:
Placement New, (+ .destructor).
- 3 then 4 objects:
Redim,
Erase.
Type UDT
Dim As String S = "FreeBASIC" '' induce an implicit constructor and destructor
End Type
' 3 then 4 objects: Callocate, Reallocate, Deallocate, (+ .constructor + .destructor)
Dim As UDT Ptr p1 = CAllocate(3, SizeOf(UDT)) '' allocate cleared memory for 3 elements (string descriptors cleared,
'' but maybe useless because of the constructor's call right behind)
For I As Integer = 0 To 2
p1[I].Constructor() '' call the constructor on each element
Next I
For I As Integer = 0 To 2
p1[I].S &= Str(I) '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
Print "'" & p1[I].S & "'", '' print each element string
Next I
Print
p1 = Reallocate(p1, 4 * SizeOf(UDT)) '' reallocate memory for one additional element
Clear p1[3], 0, 3 * SizeOf(Integer) '' clear the descriptor of the additional element,
'' but maybe useless because of the constructor's call right behind
p1[3].Constructor() '' call the constructor on the additional element
p1[3].S &= Str(3) '' add the element number to the string of the additional element
For I As Integer = 0 To 3
Print "'" & p1[I].S & "'", '' print each element string
Next I
Print
For I As Integer = 0 To 3
p1[I].Destructor() '' call the destructor on each element
Next I
Deallocate(p1) '' deallocate the memory
Print
' 3 objects: New, Delete
Dim As UDT Ptr p2 = New UDT[3] '' allocate memory and construct 3 elements
For I As Integer = 0 To 2
p2[I].S &= Str(I) '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
Print "'" & p2[I].S & "'", '' print each element string
Next I
Print
Delete [] p2 '' destroy the 3 element and deallocate the memory
Print
' 3 objects: Placement New, (+ .destructor)
ReDim As Byte array(0 To 3 * SizeOf(UDT) - 1) '' allocate buffer for 3 elements
Dim As Any Ptr p = @array(0)
Dim As UDT Ptr p3 = New(p) UDT[3] '' only construct the 3 elements in the buffer (placement New)
For I As Integer = 0 To 2
p3[I].S &= Str(I) '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
Print "'" & p3[I].S & "'", '' print each element string
Next I
Print
For I As Integer = 0 To 2
p3[I].Destructor() '' call the destructor on each element
Next I
Erase array '' deallocate the buffer
Print
' 3 then 4 objects: Redim, Erase
ReDim As UDT p4(0 To 2) '' define a dynamic array of 3 elements
For I As Integer = 0 To 2
p4(I).S &= Str(I) '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
Print "'" & p4(I).S & "'", '' print each element string
Next I
Print
ReDim Preserve p4(0 To 3) '' resize the dynamic array for one additional element
p4(3).S &= Str(3) '' add the element number to the string of the additional element
For I As Integer = 0 To 3
Print "'" & p4(I).S & "'", '' print each element string
Next I
Print
Erase p4 '' erase the dynamic array
Print
Sleep
出力:
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2' 'FreeBASIC3'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2' 'FreeBASIC3'
Imagecreate / Imagedestroy を用いた具体的な動的画像バッファの割り当て
ImageCreate は、画像用のストレージを割り当てて初期化します。
画像バッファの色深度が、描画画面の色深度と一致していれば、結果として得られる画像バッファは、どの画面モードでも、またモードが変わっても、描画手続きに使うことができます。
Imagedestroy は、画像のストレージを破棄し、割り当てを解除します。
参照: