Version 11.10
いろいろあるメモリ確保の方法
「前回は malloc() を使ってメモリを確保したけど、実際にはいろいろな方
法があります」
・ランタイム( malloc() / free() )
長所:比較的簡単に使える。例が多い。
短所:特になし。
・ API ( GlobalAlloc() 他いろいろ )
長所:細かく操作できる。特殊な使い方ができる。
短所:使うのが面倒。
・ C++ の機能( new / delete )
長所:コンストラクタ/デストラクタが呼べる。上記を代わりに使える。
短所:構文が変数ひとつと配列で違う。 C 言語では使えない。
『ほほう』
「で、あとで説明するけど、実際には最後の C++ のを使わなくちゃいけな
いんです」
『へ? じゃあ malloc() のは間違いなの??』
「ううん、あれはあれで正しいよ。ただ、 malloc() を使えない場合があっ
て、その場合には C++ の new と delete を使う事になるから、なら」
『全部 new と delete にしちゃった方がいい?』
「っていう話になるからね。ただ、 malloc() は使いやすいし、わかりやす
いから、無駄じゃないよ」
『ってことは、他はちょっと複雑なんだ』
「まぁね。まず、 API について見ておこうか」
void UseGlobalAlloc()
{
// このデータが入る領域を確保します。
const char *const DATA = "あいうえお";
// まずハンドルを取得。
HGLOBAL hGlobal
= GlobalAlloc( GMEM_MOVEABLE, strlen( DATA ) + 1 );
// 次にメモリを確保。
char *pch = (char *)GlobalLock( hGlobal );
// コピーしてから出力。
strcpy( pch, DATA );
TRACE( "%s\n", pch );
// 解放します。
GlobalFree( pch );
}
『 malloc() と同じ……じゃないんだね。なんか2段階になってる』
「そう、 API でメモリを確保するときにはふたつのステップを踏む必要が
あります。まず、ポインタを操作するためのハンドルを取得します」
// まずハンドルを取得。
HGLOBAL hGlobal
= GlobalAlloc( GMEM_MOVEABLE, strlen( DATA ) + 1 );
『ハンドルって、ウィンドウハンドルとかと同じハンドル?』
「そう、同じもの。メモリもウィンドウズシステムが管理してるから」
『同じように操作するのはハンドル経由ってことね』
「ハンドルを取得したら、次はそのハンドルを使ってメモリを確保します」
// 次にメモリを確保。
char *pch = (char *)GlobalLock( hGlobal );
『これは malloc() と同じね』
「その次の文字列のコピーも同じ。ポインタとして取得しちゃえばもう普通
のポインタとして使えるから」
『で』
// 解放します。
GlobalFree( pch );
『で解放ね。これも free() と同じね』
「だから、基本的にはランタイムを使う場合と同じ。それに実は、
malloc() とまったく同じようにも取得できるんです」
『へ?』
void UseGlobalAllocFixed()
{
// このデータが入る領域を確保します。
const char *const DATA = "あいうえお";
// 直接メモリを確保。
char *pch
= (char *)GlobalAlloc( GMEM_FIXED, strlen( DATA ) + 1 );
// コピーしてから出力。
strcpy( pch, DATA );
TRACE( "%s\n", pch );
// 解放します。
GlobalFree( pch );
}
『ホントだ、同じ GlobalAlloc() なのに直接ポインタ取ってきてる』
「第1引数に GMEM_FIXED を渡すとそうなります」
『でもなんでこっちを最初にしなかったの? これならホントに malloc()
と同じじゃん』
「そう、簡単なのはこっちだと思うんだけど、 GlobalAlloc() の戻り値は
HGLOBAL だから、ポインタとはちょっと違うんだよね。だから HGLOBAL を
取得する方法の方が、正しい方法かな、って思って」
『 GlobalAlloc() は HGLOBAL を返すのが正しい役目ってことね』
「そういうこと。で、メモリ関係の API は実はこれだけじゃないんです」
『まだあるの?』
「 VirtualAlloc() と VirtualFree() っていう API があって、こっちはか
なり複雑です」
void UseVirtualAlloc()
{
// このデータが入る領域を確保します。
const char *const DATA = "あいうえお";
// メモリを確保。
char *pch
= (char *)VirtualAlloc
( 0
, strlen( DATA ) + 1
, MEM_COMMIT
, PAGE_EXECUTE_READWRITE
);
// コピーしてから出力。
strcpy( pch, DATA );
TRACE( "%s\n", pch );
// 解放します。
VirtualFree
( pch
, 0
, MEM_RELEASE
);
}
『うわ、かなり複雑!』
「こっちは当分使わないから細かい解説はしないけど、単純にメモリを確保
するんじゃなくて、メモリ上の場所とかを指定して細かく取得できるんで
す」
『へー』
「特に、この関数で取得した領域は、他のプロセスからもアクセスできるよ
うにしたりできるんです」
『他……ってことは他から書き換えられちゃうんじゃ?? それってまずく
ない?』
「他のプログラムからは見えないから大丈夫。メモリを確保しても、そのポ
インタはわからないでしょ?」
『そういえば……』
「で、他からアクセスできるから、逆にこれを使うと他のプロセスにもアク
セスできるんです」
『あ、そっちの方がまずそう……』
「まぁね……これを使うと、たとえば他のアプリのダイアログに張り付いて
いるツリーコントロールから情報を取得したりできます」
『それはちょっと面白そう』
「実際にはそういう特殊な使い方のためにしか使わないと思うから、とりあ
えずは忘れていいよ」
『はーい』
「で、 API にはまだ HeapAlloc() と HeapFree() っていうのもあります」
『げ! まだあるんだ』
「まぁこれも直接は使わないから、ここでは説明しません」
『直接?』
「そう、こういうメモリ確保系 API を使う上で重要なことがふたつあっ
て、まずひとつは、メモリの操作は OS の操作だから、結局は API を使う
ことになります」
『結局?』
「 malloc() も API 使ってるってこと」
『あ! そうだよね、ファイル操作も確かそうだったもんね』
「 Version 5.18 ( No.083 ) で見たね。 malloc() / free() は API の
HeapAlloc() / HeapFree() を使ってます」
『それが間接的に使ってるってことなのね』
「ランタイムも結局は API を使っているっていうのは重要なことだから、
いつも気にとめておいてね。で、もうひとつの重要なこと。こっちの方が重
要なことなんだけど」
『うん』
「実は、各 API には互換性がありません」
『へ?』
「たとえば、 VirtualAlloc() で確保したポインタを GlobalFree() で解放し
ようとすると」
void UseVirtualAllocAndGlobalFree()
{
// このデータが入る領域を確保します。
const char *const DATA = "あいうえお";
// メモリを確保。
char *pch
= (char *)VirtualAlloc
( 0
, strlen( DATA ) + 1
, MEM_COMMIT
, PAGE_EXECUTE_READWRITE
);
// コピーしてから出力。
strcpy( pch, DATA );
TRACE( "%s\n", pch );
// 解放……できません。
GlobalFree( pch );
}
『げ、なんか出た!』
例外処理 (初回) は StringTest.exe (NTDLL.DLL) にあります:
0xC0000005: Access Violation。
「これは、 VirtualAlloc() で確保したポインタは、 GlobalFree() でアク
セスできないってこと。というか、たぶん中に情報がないんじゃないかな」
『情報?』
「実は、ポインタだけじゃ、確保した領域のサイズがわからないんです」
『 GlobalAlloc() とかで確保したサイズがわからない?』
「そう。だから、 GlobalAlloc() とか VirtualAlloc() した時に、そのポ
インタと一緒に確保したサイズを取っておくおんだけど、その情報は
GlobalAlloc() と VirtualAlloc() とでは別だから」
『どのサイズ解放すればいいのかわからない、と』
「というわけで、実は確保する関数と解放する関数はセットにしなきゃいけ
ないんです。というわけで次回に続く!」
/*
Preview Next Story!
*/
『あのさ、〈他のアプリのツリーコントロール〉ってやつ』
「 VirtualAlloc() 使うのだね」
『そういうののやり方は教えてくんないの?』
「そういうのまで教え始めるときりがないから……」
『でも、ちょっとやってみたい』
「やり方自体は検索すればみつかると思うよ」
『ぐぐれってことね』
「というわけで次回」
< Version 11.11 コンストラクタとデストラクタ >
『につづく!』
「で、僕はどんなサイトを見ても理解できる基本技術を教えるから」
『最終的にはその方がいい?』
「もちろん」