HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2014
0207
HK2文字列型配列はmallocの代用になるか5解決


HK2

リンク

2014/2/7(Fri) 13:30:24|NO.59733

私は個数とサイズが未定のデータをプログラムで扱おうと思っているのですが、HSPにはメモリを動的に確保する方法がありません。ですので、文字列型の配列を動的確保に代用しようと思っています。
そこで、2つの質問があります。


1つ目の質問は、文字列型の配列の要素に、バイナリデータを保存しても安全でしょうか?
特に、途中にNULL文字が入っているとおかしくなるということはないでしょうか?

2つ目の質問は、各要素のアドレスは、sdim、memexpend以外の命令と長い文字列の代入以外で変わりうるのでしょうか?
特に、バッファサイズの大きい要素に小さい長さの文字列を代入したり、memset命令などで0埋めしたりしたときに、メモリの再確保が行われるようなことはないでしょうか。


なお、HSPのバージョンは3.32です。



この記事に返信する


MillkeyStars

リンク

2014/2/7(Fri) 14:46:27|NO.59734

sdim (alloc)文字列配列変数に、バイナリを入れても問題ありません。
ただし、mes などで表示した際、NULL までしか表示できないのでご注意ください。
また、文字列操作命令・関数の一部に制約が発生いたします。

sdim で一度確保したメモリは、再度 sdim をするまでメモリサイズは有効です。
memexpand などで、大きいサイズから小さいサイズにする場合の挙動はスキップ(なにもしない)です。

注意事項として、変数自動確保などを行うと変数の内容が保障できませんので、行わないようにしてください。
sdim a,1024
a = 1024 バイトのバイナリ
a += "1" などで変数に追加した場合など、内容が保障できなくなる。

バイナリなどを扱う場合、memcpy などで直接変数内部をコピーするなど行ってください。



HK2

リンク

2014/2/7(Fri) 23:06:59|NO.59746

1つ目の質問に関しては、問題ないということですね。安心しました。

2つ目の質問に関しては、アドレスが変わらない保証はないが、確保したサイズは保障されているということですね。


ためしにmalloc命令とfree命令を作ってみました。アドレスが変わらないことが保障されていれば、このスクリプトをつかおうと思っていましたが、変わりうるのであれば、作り直そうと思います。
ご回答いただき、ありがとうございました。

2つ目の質問に関して、きっぱりとアドレスが変わりうる・変わらないという回答がもらえていないのでしばらくは未解決状態にしますが、しばらく回答がつかなければ、解決といたします。


このスクリプトを実行すると、malloc命令とfree命令の呼び出しをボタンで操作します。
左下の入力ボックスは確保ボタンを押したときに確保される領域の大きさです。
右の文字はモジュール内部の各領域の状態です。ptrは領域のアドレス、flagは使用中かのフラグ、sizeは領域のサイズです。

/* このモジュールはC言語のmalloc関数とfree関数のような命令を提供します。 malloc int p1 サイズがp1以上の領域を確保し、そのアドレスを返します。 なお、C言語と異なり、領域の確保に失敗した場合の動作は未定義です。 free int p1 mallocで確保された領域p1を解放します。 戻り値は解放処理が成功した場合0が返ります。p1がnullの場合は無条件に0が返ります。 失敗した場合は負の値が返ります。戻り値が-2の場合、二重解放の可能性があります。 */ #module "HK2mmem" #define INITBUFCNT 8 #define INITBUFSIZE 64 #define BUFEXPANDORDER 8 /* 定数 INITBUFCNT 起動時の領域の数 INITBUFSIZE 確保時の初期領域サイズ DEFBUFEXPANDORDER 領域数を増やすときの単位 */ sdim sbuf,64,1//割り当てられる要素を持つ配列 dim bflag,1//要素が割り当てられているかのフラグ。0なら未割当、1なら割り当て済み。 dim isize,1//要素の領域サイズ。 #deffunc local autoinit sdim sbuf,64,INITBUFCNT dim bflag,INITBUFCNT dim isize,INITBUFCNT repeat length(isize) isize(cnt)=INITBUFSIZE loop return #deffunc malloc int p1 len=length(sbuf)//領域数 match=-1//割り当てる領域の要素番号 start=0//高速化とコードの簡素化のためのループの初期値(サブルーチンの引数) ret=0//戻り値(サブルーチンの局所変数でもある) max=0//最大値(サブルーチンの局所変数) min=0//最小値(サブルーチンの局所変数) logmes strf("malloc:割り当て処理が開始されました") if(p1<0){ ret=0 }else{ gosub *getSuitableBufferNo//既存の領域から割り当てに適当な領域の要素番号を検索する if(stat<0){//適当な領域が見つからなかった場合 logmes strf("malloc:適当な領域が見つかりませんでした。") gosub *expandBufferCnt//領域数を拡張する match=len }else{//適当な領域が見つかった場合 match=stat logmes strf("malloc:%dが適当です\nmalloc:割り当て処理は終了しました",match) } gosub *allocateBuffer//領域を割り当てる if(stat==0){//割り当てに成功した場合 ret=varptr(sbuf(match))//領域のアドレスを返す }else{ ret=0 } } return ret *getSuitableBufferNo//既存の領域から適当な領域の要素番号を返す gosub *searchEmpty//空き領域を検索する if(stat==-1){//空き領域が存在しない場合 logmes strf("suit:空き領域は見つかりませんでした") ret=-1 }else{//空き領域が存在する場合 start=stat logmes strf("suit:最初の空き領域は%dです。",start) gosub *searchFirstFit//先頭一致する領域を検索する if(stat==-1){//十分な大きさの空き領域が存在しない場合 logmes strf("suit:十分な大きさの空き領域はありませんでした。") gosub *searchLargestEmpty//最も大きな空き領域を検索する ret=stat logmes strf("suit:最も大きな空き領域は%dです。",ret) }else{//十分な大きさの空き領域が存在する場合 start=stat logmes strf("suit:十分な大きさの空き領域は%dです。",start) gosub *searchBestFit//最良一致する領域を検索する ret=stat logmes strf("sult:最良一致は%dです。",ret) } } return ret *searchEmpty//空き領域の要素番号を返す(複数ある場合は最も先頭) ret=-1 repeat len if(bflag(cnt)==0){//未割当の場合 ret=cnt break } loop return ret *searchFirstFit//十分な大きさを持つ空き領域の要素番号を返す(複数ある場合は最も先頭) ret=-1 repeat len-start,start if(bflag(cnt)==0){//未割当の場合 if(isize(cnt)>=p1){//要求サイズ以上の場合 ret=cnt break } } loop return ret *searchLargestEmpty//最も大きな空き領域の要素番号を返す(複数ある場合は最も先頭) ret=start max=isize(start) repeat len-start,start if(bflag(cnt)==0){//未割当の場合 if(isize(cnt)>max){ ret=cnt max=isize(cnt) } } loop return ret *searchBestFit//十分な大きさを持つ最もサイズの小さい空き領域の要素番号を返す(複数ある場合は最も先頭) ret=start min=isize(start)-p1 repeat len-start,start if(bflag(cnt)==0){//空き領域である if(isize(cnt)>=p1){ if(isize(cnt)-p1<min){ ret=cnt min=isize(cnt)-p1 } } } loop return ret *expandBufferCnt//領域の数を増やす sbuf(len-1+BUFEXPANDORDER)=""; bflag(len-1+BUFEXPANDORDER)=0; isize(len-1+BUFEXPANDORDER)=0; repeat BUFEXPANDORDER,len bflag(cnt)=0 memexpand sbuf(cnt),INITBUFSIZE isize(cnt)=INITBUFSIZE loop return stop *allocateBuffer//領域を割り当てる。 if(bflag(match)==0){ memexpand sbuf(match),p1 //isizeが正しくない場合に問題が起こらないようにするため、毎回memexpandを実行 bflag(match)=1 if(p1>isize(match)){ isize(match)=p1 } ret=0 }else{//すでに割り当てられている領域を割り当てようとした ret=-1 } return ret #deffunc free int p1 if(p1){ ret=-1//戻り値 repeat length(sbuf) if(varptr(sbuf(cnt))==p1){ if(bflag(cnt)==1){ bflag(cnt)=0 ret=0 }else{ ret=-2 } } loop }else{ ret=0 } return ret #global autoinit@HK2mmem #define NUM 19 dim a,NUM dim f,NUM dim p,NUM dim s,NUM size=20 repeat NUM pos 20,cnt*24 button gosub "確保",*lisner a(cnt)=stat pos 84,cnt*24 button gosub "解放",*lisner f(cnt)=stat loop pos 0,NUM*24 input size,84,24,10 gosub *show assert stop *show color 255,255,255:boxf:color repeat NUM pos 0,cnt*24+4 mes cnt pos 148,cnt*24 if(p(cnt)){ i=cnt repeat NUM if(cnt!=i){ if(p(cnt)==p(i)){ color 255,0,0 } } loop mes strf("%p,%d",p(cnt),s(cnt)) color 0,0,0 }else{ mes strf("未確保") } loop foreach bflag@HK2mmem pos 300,cnt*20 mes strf("buf%02d:ptr=%p;flag=%d;size=%d",cnt,varptr(sbuf@HK2mmem(cnt)),bflag@HK2mmem(cnt),isize@HK2mmem(cnt)); loop return *lisner repeat NUM if(a(cnt)==stat){//確保 if(p(cnt)){ title str(cnt)+":先に解放してください。" }else{ malloc size p(cnt)=stat s(cnt)=size title str(cnt)+":"+size+"Byte確保しました。" } } if(f(cnt)==stat){//解放 free p(cnt) title str(cnt)+":freeの戻り値は"+str(stat)+"です。" p(cnt)=0 } loop gosub *show return



check

リンク

2014/2/7(Fri) 23:54:12|NO.59748


#include "kernel32.as" sdim buf, 64 // bufのサイズは64byte gosub *showptr buf = "1234567890" // 10byteの文字列を代入 gosub *showptr RtlZeroMemory varptr(buf), 64 // バッファを0で埋める gosub *showptr stop *showptr mes "bufのアドレスは " + varptr(buf) + " です。" return

こんな感じで、アドレスが変わるかもしれない処理をしてみたらどうだ?
少なくともバッファサイズが足りているのであれば、
メモリの再確保が行われるようなことはないだろう。
メモリの動的割り当てはなかなか時間がかかる処理だからな。
(プラットフォームによって差はある)



MillkeyStars

リンク

2014/2/8(Sat) 03:12:26|NO.59752

HSP のメモリマネージャに変数のサイズを変えるような命令・関数を行わなければアドレスは保証されるのではないかな。
ただ、memexpand でメモリマネージャ内のアドレス再確保しているので、その変数自体を開放して再利用する際にもし確保しているサイズより小さい物であれば、無駄が発生するって事だけかな。



HK2

リンク

2014/2/8(Sat) 18:27:26|NO.59763

変数をいじっていないのにいきなりアドレスが変わるということはなさそうという回答をいただいたので、解決といたします。

質問を書き込んだときに複数の問題を切り分けれていなかったようで、2つ目の質問は次の3つに分けなければならなかったようです。
・配列の1つの要素について、sdim、memexpand、バッファサイズ以上の文字列の代入以外で再確保が行われる(=アドレスが変わる)か
・ある要素の再確保の際、同時に他の要素でも再確保が行われるか
・要素の数が増えるとき、増える前からあった要素の再確保も同時に行われるか
もしかすると、もっと分けなければならないのかもしれませんが、あとから質問を増やすのもよろしくないので、ここで止めます。
1つ目に関しては、再確保は行われないという回答をいただきました。
2つ目と3つ目に関しては、私が投稿したスクリプトで私が試してみた分には再確保は行われていないので、たぶん行われないのでしょう。

メモリを無駄にする可能性に関して、getSuitableBufferNoサブルーチンを変更して、少なくとも改善はしようと思います。


MillkeyStarsさん、checkさん。貴重な時間を割いて回答していただき、ありがとうございます。



ONION software Copyright 1997-2021(c) All rights reserved.