メモリ管理
「変数のメモリを初期化する際にsdim命令を」と以前書きましたが、
sdim命令を使うと変数のアドレスが変わるので推奨しないとどこかで見た記憶があります。
「変数 = ""」というのは、初期化するのではなく0バイト目にNULL(文字列の終端)を入れるだけです。
普通はソレで問題ないはずなんですが、
何かの悪い条件が重なるとデータの最後にゴミ(前回のデータ)が付く場合があります。
例えば「1234567890」の入った変数に「abc」を代入すると「abc4567890」とゴミがついてしまう感じ。
これくらいの処理ではゴミがつくことはない(はず)ですが何度も再定義したり、
大きなデータの後に小さく読み込んだりするとゴミがついてしまうことがあります。
「sdim命令でもあまり好ましくないなら一体どのようにして初期化するの?」となりますが、
memset命令と呼ばれるメモリクリア命令を使用しましょう。
書式は「memset クリアする変数,クリアする値,サイズ,オフセット」です。
p1にクリアしたい変数を指定します。
p2に指定した文字でクリアします。空にするなら0を、文字でクリアするならアスキーコードを指定するか、
シングルクォートで囲んで直接文字を入れます。
p3は何バイトクリアするのかを指定します。
p4は変数の何バイト目の位置からクリアするかを指定できます。省略で先頭(0バイト目)からになります。
buf = "abcdefghijklmnopqratuvwxyz"
memset buf, , 64 ; 変数bufを初期化(NULLで埋める)
mes buf
stop
当然ですがp2には文字列は指定できません。また1バイトの文字を指定するようにしてください。
2バイトのひらがなや漢字等はエラーにはなりませんが思った通りの文字でクリアできません。
buf = "9876543210"
memset buf, 'A', 64 ; p2に65を指定しても同じ
stop
p3のサイズは変数の最大サイズを超えると他の変数までクリアすることとなってしまいます。
逆にサイズを0魔たは省略をすると、0バイトクリア(クリアしない)となるので無意味です。
別に文字列終端文字で変数すべてをクリアしなくても、表示したくない部分の先頭に終端文字をつければ
そこまでしか表示されないと言うことになるので、0バイト目だけに終端文字を付ければ
とりあえずクリアされたことと同じになりますね(厳密には全然違いますが)。
buf = "zyxwvutsrqponmlkjihgfedcba"
null = 0
poke buf, 0, null ; 終端文字(変数null)を0バイト目に入れる
stop
厳密に何が違うかは、終端文字を入れた所に違う文字を上書きすると前述のゴミがくっついてしまいます。
buf = "zyxwvutsrqponmlkjihgfedcba"
null = 0
poke buf, , null
mes buf
poke buf, 0, 'z' ; 終端文字を「z」で上書きする
mes buf
stop
memset命令に引き続き、memcpy命令に入ります。
memcpy命令とは変数の内容をコピーする命令です。
「変数=変数2でコピーできるのでは?」となりますが、変数の確保領域が大きい場合や、配列変数、
変数内の一部のデータだけをコピーしたい場合高速にコピーすることができます。
書式は「memcpy コピー先変数,コピー元変数,サイズ,コピー先オフセット,コピー元オフセット」です。
p2の変数のp5バイト目からp3バイトだけp1の変数のp4バイトの位置にコピーします。
num = 1, 2, 3, 4, 5
memcpy clone, num, 64 ; 配列変数0〜15すべてコピー
repeat 5
mes clone.cnt ; 配列の0〜4まで表示
loop
stop
コピー命令が出たついでにクローン命令も紹介しましょう。
クローン命令とは、クローン元となる変数が変更されるとクローン先の変数も自動で変わるものです。
書式は「dup クローン先変数,クローン元変数」でp1変数をp2変数として扱うことができます。
クローン先変数を変更するとクローン元変数も変わってしまう点に注意してください。
dup a, b
b = 12345
mes a ; bの変更でaが変更されたかチェック
a = 321
mes b ; aの変更でbが変更されたかチェック
stop
完全なクローンではなく、変数の型が数値型・文字列型と違う…例えばクローン元の変数が数値の場合は
クローン先の変数ではクローン元変数をアスキーコードとしてコードの文字となります。
dup buf,buf2
buf = 0 ; 数値型変数
buf2 = "a" ; 文字列型変数
mes buf ; bufに「a」が入るはずが「a」のコードが入る
stop
メモリ管理命令の紹介だけでは早く終わってしまうので実行履歴命令を紹介します。
スクリプトのどこが原因でエラーが出るかわからないと言う場合logmes命令を使用します。
書式「logmes "ログに入れるメッセージ"」で中断・終了命令かエラー終了直前までの履歴を取れます。
repeat 10
num = (9 - cnt)
ans = 100 / num
logmes "cnt = " + cnt + " 100÷" + num + " = " + ans ; 履歴を見ると最終回でエラーとわかる
loop
stop
実行するとHSPフォルダに「hsplog.txt」が作成されていると思います。
既にhsplog.txtが存在する場合はhsplog.txtの最終行に追加され上書き保存されます。
logmes命令で履歴をどのような時にどのようなオプションを付けるかはlogmode命令で指定します。
書式は「logmode ログモード,オプション」です。
p1のログモードでいつ取るかを指定します。
0でログを取りません。
1でエラー終了するときのダイアログ内容が履歴として書き込まれます。「ERROR」から始まります。
2で実行中断・終了したときに書き込まれます。「Stopped.」や「End logging.」が書き込まれます。
4でgosub・return命令の2回書き込まれます。「GOSUB」「LET」から始まります。
8でモジュールを呼び出したときに書き込まれます。「FUNC」から始まります。
16でDLLが必要な命令を使用した時に命令名が書き込まれます。「DLL」から始まります。
32で割り込み命令(onclick命令等)を使用したときに書き込まれます。「IRQ」から始まります。
512で標準の命令を使用したときに命令名が書き込まれます。「ICMD」から始まります。
1024でwidth・gettime命令等を使用したときに書き込まれます。「DCMD」から始まります。
2048で変数に値を代入したときに「変数名 <- 値」と書き込まれます。「LET」から始まります。
複数のログを取りたいときは、取りたいログモードの数字を足して指定します。
全部の履歴を取りたいときは-1を指定します。
続いてオプションを説明します。
1でどの行の履歴かがわかります。
2で実行されたときの時間がわかります。
4で実行されたときの日付がわかります。
8でファイル名がわかります。
ログモードと同じくオプションを足した値が複数オプションに、-1で全オプションになります。
width 300,0
day = ""
exist "hsplog.txt"
if strsize != -1 {
bload "hsplog.txt",day,10,2
delete "hsplog.txt"
}
logmode 512,4
title "前回起動した日付" + day
stop