#pragma twice

KAB-studio > プログラミング > #pragma twice > 210 Version 11.10 いろいろあるメモリ確保の方法

#pragma twice 210 Version 11.10 いろいろあるメモリ確保の方法

前のページへ 表紙・目次へ 次のページへ

 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 コンストラクタとデストラクタ >
『につづく!』
「で、僕はどんなサイトを見ても理解できる基本技術を教えるから」
『最終的にはその方がいい?』
「もちろん」
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。