BytePtr型の変数のメモリを解放した時にエラーが出る

返信する


答えを正確に入力してください。答えられるかどうかでスパムボットか否かを判定します。

BBCode: ON
[img]: ON
[url]: ON
スマイリー: OFF

トピックのレビュー
   

展開ビュー トピックのレビュー: BytePtr型の変数のメモリを解放した時にエラーが出る

by イグトランス » 2006年5月24日(水) 19:22

free(GetStr)を行ったら、GetStrの指す先を読み書きしてはいけなくなります。
が、GetStrは依然としてそこを指しているということに変わりはありません。
誤って読み書きしようとすると偶然できてしまうこともあります。

そういうことを未然に防ぐためにNULLを代入することがあります。
そうすれば誤ってアクセスしようとするとアクセス違反になってデバッガが検出してくれるというわけです。

しかしこの例ではどうせすぐに関数を抜けて変数も破棄されるので殆ど有難味はありません。
私は要らないと思います。

by ゲスト » 2006年5月24日(水) 19:03

> 失礼。私も話が脱線しました。≫ゲストさん

いえ、勉強なります。

コード: 全て選択

        Select Case GetWindowText( Handle , GetStr, StrByte )
            Case Not NULL
                free (GetStr)
                GetStr = NULL
        End Select
ところで、上記コードの GetStr = NULL の意味がいまいち分かりません。
free ( ) したあとは、変数の内容はヌル値になるのではないのでしょうか。

by 淡幻星 » 2006年5月24日(水) 10:41

ゲストさん さんが書きました:> 以下もfreeしてはいけないみたいでした。
>

コード: 全て選択

    Sub Invalid ( GStr As BytePtr ) 
>         SetDlgItemText ( 
>                 hMainWnd , 
>                 EditBox1 , 
>                 GStr , 
>             ) 
>         free(GStr)
>     End Sub
ここでGStrに渡されているポインタは、
動的確保されたメモリなので、free()自体は成功します。
しかし、そこでメモリを解放するということは、
その後(Invalidを抜けた後)はGStrで示されるバッファは利用不可になります。
しかし、ゲストさんのコードではその後にMsgBoxでGStrを
利用していますからエラーとなるわけです。
free()を使うタイミングは、そのポインタが示すバッファの利用が全て終わってからになります。
ゲストさん さんが書きました:> ところで、mallocは大丈夫なんでしょうか。
大丈夫だと思います。
もっとも、私はmalloc()+Zeromemory()の動作に相当するcalloc()を好んで使っています。
文字列を扱う際にはゼロクリアしてある方が安心できるので。
なお、malloc()せよ、calloc()にせよ、安全性を求めるならば、
返り値がNULLでないことを常に確認するコードを書く必要があります。
(私はよくサボリますが^^;)


≫イグトランスさん
イグトランスさん さんが書きました:C/C++ではそのとき元のメモリの扱いがどうなるかは定められていないからだそうです。
そうだったんですか。知りませんでした。
C++では基本的にnewを使っているので^^;
ABではHeap系のAPIを直に呼ぶことも多いですね~。
失礼。私も話が脱線しました。≫ゲストさん

by ゲスト » 2006年5月24日(水) 00:11

以下もfreeしてはいけないみたいでした。

コード: 全て選択

    Sub Invalid ( GStr As BytePtr ) 
        SetDlgItemText ( 
                hMainWnd , 
                EditBox1 , 
                GStr , 
            ) 
        free(GStr)
    End Sub
これで上手く出来ました。ありがとうございます。
reallocは問題があるんですね。知りませんでした。
ところで、mallocは大丈夫なんでしょうか。

by イグトランス » 2006年5月23日(火) 23:24

話はそれますが,私もreallocを使う必要は無いと思います。
reallocは例外安全の観点から使わないほうがよいという話を聞きかじったことがあります。

なんでも,reallocがもし失敗したらNULLを返しますが,
C/C++ではそのとき元のメモリの扱いがどうなるかは定められていないからだそうです。
つまり下手したら元のメモリにも読み書きできなくなる可能性が生じるというわけです。

http://msdn.microsoft.com/library/ja/jp ... frame=true
ABの現在のreallocの実装はWinAPIのHeapReAllocを呼んでいるだけですが,
HeapReAllocはきちんと挙動が定められていますね。勉強になりました。

コード: 全て選択

HeapReAlloc 関数が失敗すると、元のメモリは解放されず,元のハンドルとポインタも有効なまま残ります。
ただ,問題は将来に渡ってABのreallocがこのHeapReAllocを使い続けるかということです。
この失敗時の挙動を信じるのならば,ABのreallocではなくHeap系関数を直接呼ぶべきでしょうね。

by 淡幻星 » 2006年5月23日(火) 22:50

>セットする文字列(2回目の MsgBox で表示している文字列)は、
> 文字化けしてしまいますし、freeを行うと、そこでエラーが出ます。
realloc()するバッファの大きさが足りません。
それから、この場合のAdditionは静的確保されたメモリなので、
free()することは出来ません。

GetText()を以下のようにしてみてください。

コード: 全て選択

    Sub GetText(Addition As BytePtr)
        Dim GStr As BytePtr
        Dim pTemp As BytePtr
        GStr=GetStr(BHandle)
        MsgBox hMainWnd, MakeStr( GStr )
        '以下、変更点。---
        pTemp = GStr
        GStr = calloc( lstrlen(GStr)+lstrlen(Addition)+1 ) '※
        lstrcpy( GStr, pTemp ) '※
        free( pTemp ) '※
        Invalid( lstrcat(GStr,Addition) )
        MsgBox 0,MakeStr(GStr)
        free(GStr)
    End Sub
個人的にrealloc()は好きじゃないので、callocを使いました。
「※」の三行は次の一行で代えられるかもしれません。

コード: 全て選択

GStr = realloc( GStr, lstrlen(GStr)+lstrlen(Addition)+1 )
私が使い慣れてないせいか、どうもrealloc()の挙動が信用できないんですよね~(苦笑)。

by ゲスト » 2006年5月23日(火) 21:17

ありがとうございます、何とかもう少しで出来そうです。全体的には、

1.EditBox1の内容を取得
2.それに任意の文字列を連結
3.2.の文字列をEditBox1にセット

ということをしたいのですが、セットする文字列(2回目の MsgBox で表示している文字列)は、
文字化けしてしまいますし、freeを行うと、そこでエラーが出ます。
何が駄目なんでしょうか。宜しくお願いします。

by 淡幻星 » 2006年5月23日(火) 18:19

えっと。
ゲストさんがやりたいことがイマイチよく分からないのですが(汗)、
以下のようなことでよいのでしょうか?

・コントロール(今回はEditBox)の文字列を得る。
・その際に、Byte型の文字列を戻り値とする関数を作りたい。

だとすると、こんな感じではいかがでしょうか? まぁ、これはイグトランスさんがおっしゃっている方法ですが^^;
というか、これがオーケーなら7さんのコードもオーケーのはずで・・・、うーむ。
ところで、EditBoxに入力している文字はShift-Jisですか?
EUC等だと、取得に成功していても表示の際にはたぶん文字化けします。


なお、GetDlgItemTextStrの戻り値にテキスト、に関しては、

コード: 全て選択

Function GetDlgItemTextStrEx(hDlg As HWND, idDlgItem As Long) As String
    GetDlgItemTextStr( hDlg, idDlgItem, GetDlgItemTextStrEx )
EndFunction
ではだめでしょうか?

by ゲスト » 2006年5月23日(火) 17:24

7さんのでも文字化けしてしまいます。

そこで GetDlgItemTextStr 関数を使わせて頂こうと思うのですが、
このstr変数にではなく、関数の戻り値に取得したテキストを設定したいのですが、
よくわかりません。どうすればいいのでしょうか。

by 7 » 2006年5月23日(火) 09:25

自分はこんな感じにしてますけど。

by ゲスト » 2006年5月23日(火) 01:01

freeを呼び出すのは、戻り値を設定したプロシージャ内でないと、
無効な識別子です とエラーが出ました。

できるだけBytePtr型で取得したいので、どうしても駄目な時に、
GetDlgItemTextStrを使わせていただこうと思います。

by イグトランス » 2006年5月23日(火) 00:41

そんなときは、たとえば呼び出し側はfreeせずにBytePtrの値を返し、
戻り値を受け取った側がfreeするという方法が考えられます。

ほかにはString型を返すようにするという方法です。
StringはABがメモリの面倒を見てくれます。

#ちなみにGetDlgItemTextStr類もよろしければどうぞ。

by ゲスト » 2006年5月23日(火) 00:25

いろいろ間違っていましたね。ご指摘ありがとうございます。
この処理を関数化したいのですが、上手くいきません。

BytePtr型なので、Functionの戻り値を「プロシージャ名=戻り値」
では駄目ですよね。

lstrcpy ( プロシージャ名 , 戻り値 ) にした時、malloc=「プロシージャ名」
でメモリを確保した後は、free関数で開放する必要があると思うのですが、
そうすると戻り値がなくなってしまいます。

戻り値を設定せず、BytePtr型のグローバル変数に lstrcpy で
文字列をコピーすることで代用できるとも思いましたが、
文字化けしたものしか取得できませんでした。
どうすればいいのでしょうか。

Re: BytePtr型の変数のメモリを解放した時にエラーが出る

by イグトランス » 2006年5月22日(月) 21:29

こんばんは。
とりあえず気づいた点だけを書いておきます。無愛想ですみません。

コード: 全て選択

Dim MemoryF As Byte
Select Case MemoryF
MemoryFは関数内の変数ですから,MainWnd_CommandButton1_Clickを抜ける度にその内容は破棄されます。
そのため,これでは常にCase 0へ行ってしまいます。

もっともreallocは(freeしないまま)確保するメモリの量を変えるためにあるので,
このコードのように終わりで毎回freeするのであれば,reallocを使う必要はありません。
つまりMemoryFで場合分けする必要も無いと思います。

コード: 全て選択

EditStr=malloc(SendMessage(hEdit,WM_GETTEXTLENGTH +1,0,0))
たぶんこれがfreeでだめになる根本の原因だと思います。
EditStr = malloc(SendMessage(hEdit,WM_GETTEXTLENGTH,0,0) + 1)ではないでしょうか。
ちなみに自プログラムのウィンドウですから,WM_GETTEXTLENGTHだけでなくGetWindowTextLengthを使う方法もあります。
その方がこんな間違いをせずにすむでしょう。

コード: 全て選択

realloc(EditStr,SendMessage(hEdit,WM_GETTEXTLENGTH +1,0,0))
もし本当にreallocを使うときにはEditStr = realloc(EditStr, ...);とする必要があります。忘れやすいので,なかなか嵌ります。

コード: 全て選択

GetWindowText(hEdit,EditStr,SendMessage(hEdit,WM_GETTEXTLENGTH,0,0) +1)
蛇足ながら,どうせまたここでも文字数を得る必要がありますから,私ならWM_GETTEXTLENGTHが返した値を変数に保存しておきます。

BytePtr型の変数のメモリを解放した時にエラーが出る

by ゲスト » 2006年5月22日(月) 21:01

EditBox1の内容をBytePtr型で取得するコードを書いたのですが、
free関数でメモリ領域を解放するところでエラーが出ます。
どこが悪いのでしょうか。

ページトップ