Version 8.03
インスタンスとメモリ
「前回はインスタンスハンドルについて見てみました」
『 0x00400000 ってアドレスから、実行ファイルそのものが入ってたんだよ
ね』
「そうそう。で、この値の切りの良さとかの理由を今回は解説します。
えーっとまず、このプログラムでちょっと確認してもらおうかな」
#include <Windows.h>
#include <stdio.h>
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// インスタンスハンドルを文字列化します。
char pchMsg[1024];
sprintf( pchMsg, "HINSTANCE : %x", p_hInstance );
// それを表示。
MessageBox( NULL, pchMsg, "WinMain()", MB_OK );
return 0;
}
『んーと、インスタンスハンドルを sprintf() で文字列に入れて、それを
ダイアログに出すわけね』
「 sprintf() については Version 5.07 ( No.072 ) を参照」
『あれ? でもその時って stdio.h なんてインクルードしなかったよね』
「あのときは MFC を使ってたから。 Version 6.04 ( No.104 ) でちょっと
触れたけど、 MFC はたいがいのランタイムをインクルードしてるから」
『 MFC を使わないときは、 Windows.h も、 stdio.h もインクルードしな
きゃいけないわけね。めんどー』
「それはまぁ MFC の便利なところだけど」
『あとさ、なんでわざわざダイアログで出すの? この前みたくデバッグ
ウィンドウで見ればいいじゃん』
「それは、いくつも起動するから。 SimpleDialog\Debug にある
SimpleDialog.exe をいくつも起動して」
『ほいほいほい。全部ダイアログ出て……全部 40000000 だ、インスタンス
ハンドル』
「まずこれを確認してもらいたかったんだよね。インスタンスハンドルはア
プリ固有で、実行ファイルそれぞれじゃ違わないってことを」
『ん? 当たり前っぽい気もするけど』
「あ、そーか。じゃあ今度はこれ」
#include <Windows.h>
#include <stdio.h>
#include <time.h>
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// ランダムな値を取得。
time_t lTime_t;
time( &lTime_t );
srand( lTime_t );
int i = rand();
// それを文字列化。
char pchMsg[1024];
sprintf( pchMsg, "int i : %x, %d", &i, i );
MessageBox( NULL, pchMsg, "WinMain()", MB_OK );
return 0;
}
『変数 i にランダムな値入れて、その i のアドレスと i の中身を出力、
ね』
「乱数については Version 7.19 ( No.139 ) を参照」
『 time.h は time() とか使うためのだよね。これも?』
「そう、これも MFC はインクルードしてくれてました」
『そんなんばっかねー。で、実行実行実行っと。お、全部アドレスは同じだ
けど、値はランダムだから違うね』
「この辺が重要。まず、値がそれぞれ違うってことは、実行したそれぞれで
i の中身が違うってこと」
『? 当たり前……あ、なんかさっきのインスタンスハンドルと矛盾?』
「ってゆーか、こっちを中心に考えた方がいいかな」
『どゆこと?』
「この前見たメモリウィンドウ、あんなふうにメモリは広がってます」
『うん、広がってます』
「で、あの広がってる世界が、実行した分だけ作られるんです」
『え!? だってメモリの中じゃ……』
「そう、そこが重要。いつもメモリウィンドウで見てたのは、仮想的なメモ
リなんです」
『メモリのパチモン!?』
「偽物じゃなくて、見え方が違うって感じかな。実際にメモリ上にああいう
風に置かれてはいるけど、アドレスは違うって感じ」
『いまいちよく分からない……』
「そうだね、たとえば」
ハードウェアとしてのメモリ
[アドレス]
1001
1002 ひとつめに実行したアプリの i
1003
1004
1005
1006 ふたつめに実行したアプリの i
1007
1008
「って感じにハードウェア上のメモリ上には、実行した分だけ中の変数 i
が作られます」
『でも、アドレスは同じだった』
「それは」
ひとつめに実行したアプリの仮想メモリ
[アドレス]
00005001
00005002
00005003 i (ハードウェア上では 1002 に置かれてる)
00005004
00005005
ふたつめに実行したアプリの仮想メモリ
[アドレス]
00005001
00005002
00005003 i (ハードウェア上では 1006 に置かれてる)
00005004
00005005
「って感じに、仮想的なメモリがそれぞれに割り当てられてるから」
『ってことは、ひとつの変数にアドレスがふたつあるの?』
「実際にはそういう事。 i って変数が 5003 ってアドレスと実際のハード
としてのメモリ上のアドレスとふたつ。ただ、ハード上のは見えないから考
える必要はないけどね」
『でも考えてるじゃない』
「そりゃ、こういうこと知ってないといけないし」
『ぶー』
「で、インスタンスハンドルに話を戻します」
『ってことは』
ひとつめに実行したアプリの仮想メモリ
[アドレス]
00400000 実行ファイルの中身
00400001
00400002
ふたつめに実行したアプリの仮想メモリ
[アドレス]
00400000 実行ファイルの中身
00400001
00400002
『って感じに置かれてるわけね』
「そうそう、そういうこと。つまり、この値は実行ファイルをいくつ作って
もそれぞれ同じ、だけどひとつの場所じゃなくそれぞれ複製なんだってこと
に注意してね」
『なんか無駄って感じも』
「で、このアドレスの場所、つまりインスタンスハンドルの値、実はある程
度変えられるんです」
『へ?』
「デフォルトは 0x00400000 なんだけど、【プロジェクトの設定】ダイアロ
グの【リンク】−【アウトプット】のページを見て」
『むむ、難しそう……』
「この【アウトプット】ページの【ベースアドレス】に 0x410000 を入れて
OK で閉じてリビルドして」
『ほいほいほい。んで実行。あれ?』
「あ、インスタンスハンドルを見るならブレークポイントを置くか、最初の
方のプログラムでビルドするかしないと」
『さっきのに戻してビルドして〜っと、んで実行。お、インスタンスハンド
ルが 410000 になった!』
「複数実行すると」
『全部 410000 だ!』
「ってわけで、インスタンスハンドルはこういうふうに自由に変えることが
できるんです」
『自由って割りには…… 0x400001 じゃないの?』
「この値は 64 キロバイト単位でないといけない、っていう制限があるから
ね。実際に 0x400001 で試してみると」
『あ、無理矢理 0x410000 にされちゃった……』
「で、これをメモリウィンドウで見ると」
『うん、 0x00410000 から実行ファイルが置かれてる。その前は相変わらず
?? になってる』
「そう、その ?? も大事。【ベースアドレス】ってことは」
『アドレスの底、そっか、ここがアドレスの最初!』
「そうなんです。これ以前のアドレスは無効」
『無効?』
「ハードウェア上のメモリと違って、仮想メモリならこういうのは自由にで
きるからね」
『でもさ、それって逆じゃない? 仮想なら、どんなテキトーなアドレスで
もいいんだから、こんな無駄なことしなくても』
「うんうん、その辺は次回に続く!」
『続くんすか!?』
/*
Preview Next Story!
*/
『なんかインスタンスハンドルの話、気合い入ってるね』
「これもメモリやポインタの話と関連してるからね」
『 C++ の難しいとこってよく言われるもんね〜』
「まぁ、簡単ってわけでもないけど」
『ってゆーか、ハードウェアのメモリって、見えないし』
「その辺は想像してもらうしかないけど」
『けど?』
「仮想メモリは見えるわけだから、その辺はしっかりとね」
『 VC のメリットってわけね』
「というわけで次回」
< Version 8.04 プロセスとインスタンスハンドル >
『につづく!』
「この辺は徹底的にやるからねー」
『でもちょっと食傷気味……』