プログラム講座 初級編10

- メモリについて その2(メモリを確保しよう) -

 初級編10です。今回はメモリを確保するプログラムを作成します。割と簡単に扱えそうなFuture BASICですが、ちょっと凝ったことやMac寄りのプログラムを作成しようとすると、必ず「メモリの確保、破棄/ハンドルの使用方法」が出てきます。初級編ですので、なるべく簡単なサンプルで説明します。




メモリを確保するには?
 Future BASICに限らずMacで(ハンドルを使って)メモリを確保する場合、必ず以下の手順に従います。
【1】メモリを確保
【2】ハンドルをロック
【3】メモリ内容を操作
【4】ハンドルロックを解除(アンロック)
【5】メモリを解放
 この手順を守らないと、メモリを解放し忘れたりシステムエラーの憂き目にあいます。上記の手順でミスした場合、以下のような症状が発生します。
【1】
メモリがきちんと確保できていないのに処理を続けるためプログラム、データの破壊、バスエラーが発生する
【2】
メモリブロックが移動してしまうため異なるメモリ領域に書き込みを行ってしまう。プログラム、データの破壊およびバスエラー、アドレスエラーが発生する。
【3】
バイト単位の処理を間違えるとプログラム、データの破壊およびバスエラー、アドレスエラーが発生する。
【5】
メモリはあるが解放されないため、使用できるメモリが減少していく。(メモリにごみが残るという表現の仕方もある)
 どれを取っても、かなり致命的なエラーになり得ます。最悪の場合、システムエラーさえ発生せずに完全に停止してしまいます。メモリを確保し、操作する場合どのようにメモリを確保したかは自己管理でシステムはほとんど管理しません。メモリを操作する場合は注意しましょう。

 それでは上記の順番に説明していきます。



【1】メモリを確保
 メモリを確保するには以下のようになります。

myHandle& = FN NEWHANDLE(確保するバイト数)

 myHandle&は必ず「ロング変数」でなければなりません。要するに変数の最後に&を付加しなさい、という事です。
 確保するバイト数は1Kバイトならば1024といった具合に指定します。64Kバイト確保したければ64*1024と記述しておけばOKです。
 メモリが無事に確保できた場合はmyHandle&に0以外の値、つまりポインタへのアドレスが入ります。ハンドルが確保できたか出来ないかはmyHandle&が0かどうかを調べればよいわけです。特に「多分確保できるだろう」と思って記述していくと、泥沼にはまることがありますので、必ずハンドルの値を調べてメモリが確保できたかどうか確認しましょう。



【2】ハンドルをロック
 無事にメモリが確保できたら「ハンドルをロック」します。ハンドルをロックするには以下のようにします。

err% = FN HLOCK(myHandle&)

 メモリを操作する場合、必ずハンドルをロックしてください。そうしないと、とんでもないメモリアドレスを読み出したり、書き込んだりしてしまいます。



【3】メモリ内容を操作
 メモリを確保しハンドルもロックしたら、やっと確保したメモリ領域を操作することが出来ます。まずはハンドルが示すポインタの値を読み出します。ポインタを読み出したら、ポインタが示すアドレス内容を読み出します。これでメモリ内容を読み出すことが出来ます。実際には以下のようになります。

pointer& = PEEK LONG(myHandle&)
theValue% = PEEK(pointer&)  
(読み出す)
POKE pointer&,theValue%
(theValue%の下位8ビットを書き込む/1バイトを書き込む)
POKE WORD pointer&,theValue%
(theValue%の値を書き込む/2バイトを書き込む)
POKE LONG pointer&,myValue&
(myValue&の値を書き込む/4バイトを書き込む)

 ポインタもハンドル同様メモリアドレスを示しますのでロング変数でなければなりません(変数の最後に&が付く)。後はポインタが示すメモリアドレスの内容を読み出し/書き出します。メモリ内にどのように書き込まれるかはバイト/ワード/ロングワードによって異なります。今回は説明しません。別の機会に実例を交えて解説したいと思います。しばらくは「1バイト単位」でメモリ操作を行ってみてください。
 非常に重要な点があります。確保したメモリ範囲外の領域を操作する事ができてしまいます。1バイトだけ確保しておき、次の1バイトを読み出すこともできてしまいます。ここらへんはシステムは管理せず、やはり自己管理になりますので注意しましょう。

theValue% = PEEK([myHandle&])という書き方もできます。こちらの方がハンドルを使って読み出しています、といった感じでわかりやすいかもしれません。[〜]はPEEK LONGの短縮形(ショットカット)でtheValue% = PEEK(PEEK LONG(myHandle&))と同じです。



【4】ハンドルロックを解除(アンロック)
 メモリを操作し終わったら「ハンドルのロックを解除(アンロック)」しましょう。ハンドルのロックを解除するには以下のようにします。

err% = FN HUNLOCK(myHandle&)

 必ずハンドルのロックを解除しなければいけないわけではありません。が、初心者の内はハンドルをロックしたらアンロックする、というパターンで覚えてしまいましょう。



【5】メモリを解放
 使い終わったら必ずメモリを解放してください。ハンドルを破棄するとも言います。メモリを解放するには以下のようにします。

err% = FN DISPOSHANDLE(myHandle&)

 メモリを確保したら、必ず解放する事を忘れないようにしましょう。ちなみにFN DISPOSHANDLEでFN DISPOSEHANDLEではありませんのでスペルミスに注意しましょう(^^)。
 以下に今までの手順とToolbox/Future BASICの命令を対比させておきます。太字の部分はToolboxの呼び出しです。

処理内容命令/Toolbox呼び出し
【1】メモリを確保myHandle& = FN NEWHANDLE(確保するバイト数)
【2】ハンドルをロックerr% = FN HLOCK(myHandle&)
【3】メモリ内容を操作POKEまたはPEEK
【4】ハンドルロックを解除err% = FN HUNLOCK(myHandle&)
【5】メモリを解放err% = FN DISPOSHANDLE(myHandle&)



終わりに
 以下のサンプルプログラムは適当にメモリを確保し1バイト読み込んで表示するものです。
 今回は本当に単純なサンプルですが非常に重要なサンプルです。次回の初級編11ではテキストファイルが「何行あるか?」をチェックするプログラムを作成します。ファイルから一度にデータを読み込み確保したメモリから読み出し何行あるかどうか調べるというものです。



今回のプログラムリスト
' ' "メモリを確保するサンプル" ' LOCAL FN getMemory(theSize&) DEFSTR LONG PRINT "----------------------------------" PRINT theSize&;"バイトのメモリを確保します" ' "ハンドルを使ってメモリを確保" myHandle& = FN NEWHANDLE(theSize&): ' "theSize&バイトメモリを確保します!" LONG IF myHandle& = 0 PRINT "残念メモリは確保できませんでした" XELSE PRINT "メモリを確保できました" err% = FN HLOCK(myHandle&): ' "ハンドルをロック!(重要)" pointer& = PEEK LONG(myHandle&): ' "ポインタの値を読み出す" theValue% = PEEK(pointer&): ' "メモリ内容を読み出す" PRINT "ハンドルの値:";HEX$(myHandle&) PRINT "ポインタの値:";HEX$(pointer&) PRINT " メモリの値:";theValue% err% = FN HUNLOCK(myHandle&): ' "ハンドルをアンロック" err% = FN DISPOSHANDLE(myHandle&): ' "ハンドルを破棄します!" END IF END FN FN getMemory(100): ' "100バイト確保してみます" FN getMemory(2000): ' "2000バイト確保してみます" FN getMemory(1024*1024*60): ' "60Mバイト確保してみます" PRINT "マウスボタンをクリックすると終了します" WHILE FN BUTTON = _false:WEND