不思議な、不思議な構造体サイズについてのお話。実は自分自身よく分かっていない。
今回使う構造体は以下の通り。
Private Type udtTest strBuff1 As String strBuff2 As String * 20 lngNum As Long strBuff3 As String intNum As Integer End Type 何の変哲も無い構造体である。 この構造体の各メンバのサイズ、構造体のサイズを求めると下のようになった。え゙〜っ!!て感じになるねこりゃ。だって、各メンバサイズを合計した値と構造体自体のサイズが違うんだもん。
Len、LenBそれぞれ各メンバサイズの単純合計と実際の構造体サイズには8バイトのズレがある。問題の箇所は見れば一目瞭然!! 動的文字列変数 strBuff1 と strBuff3 だ。単純計算で2で割ると、構造体のサイズには4バイトとしてカウントされている。果たして、この4バイトはどこからきているの?4バイトのデータ型は、Long、Single、Object がある。まさか、オブジェクト型として保持されてはいないだろう。とすると Long か Single だ。 ここからは推測になっちゃうんだけど、4バイトの分は変数のアドレスだと思うんだよね。VB5.0以降でStrPtr、VarPtr、ObjPtrという隠し変数(VB4.0では VarPtr が用意されているが、使用するには宣言が必要)がありそれぞれ、文字列が存在するアドレス、変数のアドレス、オブジェクトのアドレスを返してくれるんだけど、戻り値はすべてLong型なんだよね。どうであろうか。 構造体で、文字列変数を使用する場合、固定長変数は確保したサイズ分をカウントし、可変長変数の場合はその変数が存在するアドレスの分(Long型=4バイト)をカウントするのではないだろうか。 さて、これが本当かどうか調べてみよう。CopyMemory API関数を使えばできそうである。早速、コーディング開始!! Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
'---------------------------------------------------------------- ' 関数名 : DumpMemInfo ' 機能 : 指定メモリー位置から指定サイズ分、1バイトずつ表示する ' 引数 : (in) gPtr … メモリー ' (in) VarSize … サイズ ' 戻り値 : なし '---------------------------------------------------------------- Public Sub DumpMemInfo(ByVal gPtr As Long, ByVal VarSize As Long) Dim i As Integer Dim byteBuff(0) As Byte For i = 0 To VarSize - 1 Call CopyMemory(byteBuff(0), ByVal (gPtr + i), 1) Debug.Print Hex$(byteBuff(0)) & Space$(1); Next i Debug.Print "" End Sub 関数は上の通りでよろしい。さて、実際に調査してみましょう。 Private Sub Command1_Click() Dim udtTest As TEST With udtTest .strBuff1 = "Test" .strBuff2 = "TEST" .lngNum = &HFCFDFEFF .strBuff3 = "てすと" .intNum = &HFEFF End With 'String型メンバ変数のポインタを16進数表示する Debug.Print "? udtTest.strBuff1 : " & Right$("00000000" & Hex$(StrPtr(udtTest.strBuff1)), 8) Debug.Print "? udtTest.strBuff3 : " & Right$("00000000" & Hex$(StrPtr(udtTest.strBuff3)), 8) '構造体データを丸っとダンプする Call DumpMemInfo(VarPtr(udtTest), LenB(udtTest)) End Sub
こちらの環境で実行した結果、以下の通りになった。
? udtTest.strBuff1 : 001849AC
上の値を変数(可変長文字列変数は4バイトと仮定)にしたがって分かりやすく分類してみると、
AC 49 18 0
となる。あれっ? Long、Integer 変数に指定した値とメモリーに保持されている値、表示された可変長文字列変数のアドレスとメモリーに保持されているアドレスの値がひっくり返っているね。また、固定長文字列変数は思いっきり Unicodeとして保持されているし、はじめの4文字以降はきちんとChr$(0)で埋められている。非常に面白いね。これは覚えていても損はない知識だね。 さて、話を元に戻そう。上の数字を見ると先ほどの予想が当たっているね。え、何がって?だから、構造体の動的文字列変数は文字列が存在するアドレスを保持するということさ。例えば、? udtTest.strBuff1 : 001849AC と AC 49 18 0。ひっくり返ってはいるものの明らかにアドレスでしょこれは。従って、構造体において可変長文字列変数は4バイトとして扱われる、ということが言えるのではないか。 |