FreeBASIC マニュアルのトップに戻る

FreeBASIC ProPgDynamicMemory

目次→教本→プログラマーのための案内Dynamic memory management with FreeBASIC←オリジナル・サイト

FreeBASIC での動的メモリ管理 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

FreeBASIC による 動的メモリ 管理(割り当て・解放)について

序文:
FreeBASIC では、3 種類の基本的なメモリ割り当てをサポートしています:
- 静的割り当ては、静的変数とグローバル変数に対して行われます:
メモリの割り当ては、プログラムの実行時に一度だけ行われ、プログラムの存続期間中は持続します。
- スタックの確保は、手続きのパラメータやローカル変数に対して行われます:
対応するブロックに入る時にメモリが確保され、ブロックから出る時に解放されることが、必要な回数繰り返されます。
- このページでは、動的割り当てについて説明します。

静的割り当てとスタック割り当てには、2つの共通点があります:
- 変数のサイズが、コンパイル時にわかっている。
- メモリの割り当てと解放は自動的に行われます(変数がインスタンス化されてから破棄されるとき)。
ユーザーはこのような変数の破棄を予測することはできません。

ほとんどの場合、これでいいと思います。しかし、これらの制約のどちらか一方が問題となる状況もあります(必要なメモリがユーザーの入力に依存する場合、サイジングは実行中にしか決められません)。

コンパイル時にすべてのサイズを宣言しなければならない場合、最良の方法は、必要な変数の最大サイズを推定して、それで足りることを願うことです。
しかし、これは、少なくとも 3つの理由から、悪い解決策です:
- 第一に、メモリが十分に使われなければ、無駄遣いにつながります。
- 第二に、ほとんどの通常の変数は、メモリ中のスタックと呼ばれる部分に割り当てられます。
プログラムのスタック・メモリの量は、通常、かなり少なく設定されています(デフォルトでは 1MBまたは 2MB)。
これを超えると、スタック・オーバーフローが発生し、プログラムが中断してしまいます。
- 第三に、最も重要なことですが、これは人為的な制限やオーバーフローにつながる可能性があります。
必要なメモリが予約されたメモリよりも大きくなった場合、どうなるでしょうか(プログラムの停止、ユーザーへのメッセージの表示、...)。

幸いなことに、これらの問題は、メモリの動的割り当てで簡単に解決できます。
動的メモリ割り当てとは、プログラムを実行する際に、必要に応じてオペレーティング・システムにメモリを要求する方法です。
このメモリは、プログラムの限られたスタック・メモリからではなく、ヒープと呼ばれるオペレーティング・システムが管理するはるかに大きなメモリ・プールから割り当てられます。
最近のマシンでは、ヒープの大きさは数ギガバイトにもなります。

動的メモリ割り当てに関するキーワード
動的なメモリの割り当て/解放のためのキーワードは 2種類あります:
- Allocate / Callocate / Reallocate / Deallocate:
単純な定義済みの型やバッファ(数字の定義済み型、ユーザー・バッファなど)に対して、生のメモリ割り当てと解除を行います。
- New / Delete:
オブジェクト型(可変長文字列、UDT など)に対して、メモリ割り当て + 構築、次に、破棄 + 割り当て解除、行います。

同じメモリ・ブロックを管理する場合、この 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 は、画像のストレージを破棄し、割り当てを解除します。

参照:
プログラマーのための案内に戻る
目次に戻る
←リンク元に戻る プログラム開発関連に戻る

ページ歴史:2022-07-10 12:51:50
日本語翻訳:WATANABE Makoto、原文著作者:fxm

ホームページのトップに戻る

表示-非営利-継承