ここに記載されている計測値等は何れも作者環境(OS:WinMe/CPU:PentiumIII/ROM:64MB/Compile:HSP2.55)で確認したものです。
戻るbsaveの第二引数は変数ならどんな形式でも指定出来ます。ここにはそれぞれの形式で保存した場合の保存フォーマットについて覚書として記録しています。
なお、第二引数とする変数は説明上でbufとし、\0はnull文字(ASCII Code=0)を意味しています。ダンプリストとはASCII Codeのみで文字列を表現して、それを1バイトごとに16進数2文字ずつで表現したものです。
(例) "test"→74 65 73 74 00
最後の00は\0のことで文字列の終わりを意味しています。
この形式はHSPヘルプでも書かれている、標準指定形式です。bsaveの第三引数でサイズ指定しない場合はsdimサイズをそのまま保存します。
sdim buf,32 ;32バイト確保('\0'*32) buf="test" ;"test"+"\0"*28バイト
なお、無宣言文字列変数は文字代入時にsdim buf,64としたと認識されます。HSP2.55から認識が寛容になったため、今まではsdim buf,64:buf="test":buf=10などとするとエラーだったのが、毎回形式が変わります。
この時宣言メモリ範囲は数値では配列に変換され、文字列にすると全てが単一文字列エリアとされてしまいます。文字列→数値配列変換において端数は切捨てられます。
調べる前はヘッダやフッタが識別用にあるのかと思っていたらどれにも全くありませんでした。なので保存データ=内部データ(実体)と見なせます。
数値を4バイトに分解して(といっても内部では最初からそうなのですが)、リトルエンディアン(小さい数字を表すもの順)で保存します。
dim buf ;変数宣言(内部では00 00 00 00) buf=258 ;数値代入(内部では02 01 00 00) ;buf=1 ;数値代入(内部では01 00 00 00) ;buf=2 ;数値代入(内部では02 00 00 00) ;buf=10 ;数値代入(内部では0A 00 00 00) ;buf=16 ;数値代入(内部では10 00 00 00) ;buf=255 ;数値代入(内部ではFF 00 00 00) ;buf=256 ;数値代入(内部では00 01 00 00) ;buf=257 ;数値代入(内部では01 01 00 00)
数値配列変数です。この場合数値がint個連続しているのと同じ事になります。バイト数は(配列の個数)×4バイトになります。
dim buf,5 buf=1,2,3,4,5 ;内部では01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 ;ちなみにbuf=1,2,3,4,5はbuf.0=1:buf.1=2:...:buf.4=5と同意です。
数値型多次元配列は右の配列から順に記録されていました。サイズは(配列の全要素数)×4バイトです。ちなみに配列の全要素数を求めるには配列の各次元の最大をかければいいだけです。
dim buf,5,2 ;5*2*4=40バイト repeat 2:a=cnt repeat 5:t=a*5+cnt buf.cnt.a=t loop:loop ;上のループ実行結果 ; buf.0.0=0 buf.1.0=5 ; buf.0.1=1 buf.1.1=6 ; buf.0.2=2 buf.1.2=7 ; buf.0.3=3 buf.1.3=8 ; buf.0.4=4 buf.1.4=9 ; ;このデータを保存すると ;00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 00 00 00 06 00 00 00 07 ;00 00 00 08 00 00 00 09 00 00 00 ;とキレイに並ぶ。 dim buf,2,3,4,2 ;2*3*4*2*4=192バイト ;記録は(0.0.0.0),(1.0.0.0),(0.1.0.0),(1.1.0.0),〜,(1.1.3.1),(0.2.3.1),(1.2.3.1)という順で保存されます。
文字列配列変数。バイト数はsize×intになります。
sdim buf,8,5 ;8*5=40バイト buf="0test","1blue","2red","3yellow","4green" ;保存データは ;30 74 65 73 74 00 00 00 31 62 6C 75 65 00 00 00 32 72 65 64 00 00 00 00 ;33 79 65 6C 6C 6F 77 00 34 67 72 65 65 6E 00 00 ;になります。
文字列型多次元配列。4,5の結果から予想できる通りの結果になります。サイズは(配列の全要素数)×sizeです。つまりsdimの第二引数以下を全て掛け合わせるとバイト数になります。
sdim buf,8,5,2 ;(5*2)*8=80バイト repeat 2:a=cnt repeat 5:t=a*5+cnt poke buf.cnt.a,0,t loop:loop ;上のループ実行結果(全てアスキーコード) ; buf.0.0=0 buf.1.0=5 ; buf.0.1=1 buf.1.1=6 ; buf.0.2=2 buf.1.2=7 ; buf.0.3=3 buf.1.3=8 ; buf.0.4=4 buf.1.4=9 ; ;このデータを保存すると ;00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 ;04 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00 ;08 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 ;とキレイに並ぶ。 sdim buf,8,2,3,4,5 ;8*2*3*4*5=960バイト ;記録は(0.0.0.0),(1.0.0.0),(0.1.0.0),(1.1.0.0),〜,(1.1.3.4),(0.2.3.4),(1.2.3.4)という順で保存されます。
どうやらsdimもdimの違いは表面的に文字列か否か、という認識でしかないようです。
dim buf,int1,int2...はsdim buf,int1/4,int2...というように受け取り、処理しているような様相に見えてきました。具体を述べると、bload "filename",bufとした時、bufをメモリ配列としてのみ考え、他からメモリ内容を参照させるためのみに扱う場合は、変数代入の時に形式変換(初期化)が起こらないmemcpyを使い、表示命令に渡す変数の形(文字列か 数値か)のみ変えてやれば問題なく動作します。
参照として使う場合、dim配列のほうが有利かもしれません。
変数保管法を記述していてふと思いついた事ですが、配列文字列変数で指定サイズ以上の文字列を指定するとどうなるでしょうか?
たとえば、
sdim sa_text,8,8
とするとsa_textは単位あたり8文字(nullを除くと7文字)、要素数8の一次元配列になります。もしここで
sa_text.2="this is a test script"
とするとどうなるのでしょうか?内部的に見ればメモリ64バイト確保している中から出ていないのでエラーではありません。しかし、表示しようとすると…
dialog sa_text.2
なぜかこれで"this is a test script"と表示されます。内部ではsa_text.2の次のメモリはsa_text.3,4,5..なのでこれも表示して みると…
dialog sa_text.3,,"3" dialog sa_text.4,,"4" dialog sa_text.5,,"5"
3は"a test script"、4は"cript"、5は何も表示されません。もうお分かりでしょうか?文字列は最後がnull文字という約束があるので2,3では配列を飛び越えてしまい、4の6文字目まで読み込んでいるのです。
(図A)配列におけるメモリ確保の概念図確保メモリ | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
配列番号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
メモリ実体 | ******** | ******** | This is | a test s | cript*** | ******** | ******** | ******** |
注釈:確保メモリの"-"は1byteを意味し、メモリ実体の*はNULL文字(\0)を意味しています。
戻る変数サイズの32KB制限は大幅に緩和され、作者環境ではHSP2.55現在268435344バイト(256MB-112Bytes)まで扱う事が出来ました。しかし、メガ単位になるとメモリ圧迫が激しく、さらに一度に処理するべき量が増える為、memset,memcpyでさえ(memset,memcpy)関数内を抜けるのに数秒〜数十秒かかります。
refstrのサイズは65536バイトまで拡張されたと公表されたのですが、それに伴いモジュール定義関数に渡すstr文字列のサイズも拡大したのかなと思っていたら256バイトでした。あれ?と思い標準命令(existなど)の"filename"などを調べてみたらなんと32KBまでOKでした(それを超えた文字列を渡すと強制終了)。これは当然なんですけどね、ファイルを扱う標準命令が絶対アドレス(=ドライブから始まるファイル名)の長さに負けてはお手上げですし。
ファイルの絶対アドレスを渡そうとすると、現在のWindowsOSのフォルダの多構造化、進むファイル名長の増加などから考えてstrでは応用が利かないので当面はvalを選択するつもりです。
詳しい理由は、strを使うとカレントディレクトリと相対位置ディレクトリ指定からchdirを使い無理に255文字以下にすることも可能ですが、内部で行うと毎回処理開始時に負担をかけてしまいます。外部からそうするのは当然面倒ですし、方々のディレクトリにアクセスする必要のあるスクリプトではやはり負担がかかります。
これはHSP標準搭載関数にもいえることですが、変数のサイズが以前よりも格段に大きなサイズを獲得できるようにはなりましたが、前述のようにあまり大きすぎるサイズを確保するべきではありません。実際どれくらいが適当かというと、当然ですが処理を圧迫しない範囲であること。CPU330〜400MHzでは大体大きくても5MB(5*1024*1024)くらいにすべきでしょう。しかし必要性がない限り32KBで抑えているのが賢い方法であると思います。(これにより前述の標準命令に起こる超過文字列指定による強制終了も避けられる、という理由もあります)
戻る2.55になって、変数形式の違いによるエラーがなくなりました。といっても標準命令に渡す引数は未だにエラーがでるものが多いようです(dialogやmesで数値を渡すなど)。
内部的に文字列も数値も同様の扱い方をするようになったようで、t=0:t="test"やsdim ts,32:ts=32でも正常に実行されてしまいます。はじめの例ではt=0で数値型(配列最大数16)として宣言されて数値代入され、t="test"では64バイトの文字列変数として再初期化されてsdim t,64:t="test"とされた文字列になります。
次の例では、sdim ts,32で文字列変数として宣言されたtsをts=32でdim ts,8と再初期化しています。文字列から数値に変わる場合は確保メモリ数を配列に割り当てて、配列数=(確保メモリ数)/4とし、数値から文字列に変わる場合はその反対で確保メモリ数=(配列数)/4として単純な文字列エリアになります(無配列)。つまりコンパイラ(HSP)は、dimにしろsdimにしろ、宣言したメモリ領域は不変で形式だけを変えたようにふるまいます。ここで危険なのはsdim宣言による最大数非決定変数(sdim val)です。この場合メモリは全く確保されません。しかも関数から文字列を表示させようという場合に下手をすると文字列が無限というような判断が下されます(null文字が末尾にないとそうなり ます)。
戻る