#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のトップページをご覧ください。