.NETの不思議

今日遭遇したCLRの不思議
通常System.Booleanは4バイトだと思うじゃない
実際、普通の場合は4バイトだし、Marshal.SizeOf(Boolean)でも4が返る
それがなぜかアライメントされず1バイトになる時があった

Public Structure A

    Public b As Boolean
    Public i16 As UInt16
    Public i8 As Byte
    Public i32 As UInt32

End Structure

こんな構造体だと普通はこうなる

  • bは1〜4バイト目に配置
  • i16は5〜6バイト目に配置
  • i8は7バイト目に配置
  • 8バイト目はパディング
  • i32は9〜12バイト目に配置


この構造体を共用体用に使いたいからってこうする

<StructLayout(LayoutKind.Explicit)> _
Public Structure Test

    <FieldOffset(0)> Public x As A
    <FieldOffset(0)> Public y As UInt32
End Structure

そうするとなぜかTest.xのアライメントがAと変わってしまう

  • Test.x.bは1バイト目に配置
  • Test.x.i16は3〜4バイト目に配置
  • 5〜6バイト目はパディング
  • Test.x.i8は7バイト目に配置
  • 8バイト目はパディング
  • Test.x.i32は9〜12バイト目に配置


なぜかTest.x.bが1バイトで済まされる
でもMarshal.SizeOf(Test.x.b)は4と返してくる
もちろんLayoutKind.Explicitを指定しなければこんな事は起きないし
Test.yの型をAにしてもこんな事にはならない
なんだそりゃ・・・


コンソールの入力を読みとるためにINPUT_RECORD構造体を書いてて気づいた
意味が分からない・・・
↓モロそれだと思われる同様の質問
http://stackoverflow.com/questions/1602899/what-is-the-difference-between-structures-containing-bool-vs-uint-when-using-pinv


ilレベルでも同じ事が起きるのでCLRの問題なのか?
BooleanをInt32とかで置き換えて、本命の名前のプロパティを作ってやれば解消は可能
あとは地味にLayoutKind.Explicitで指定していくか、、、

Private b_ As UInt32
Public Property b As Boolean


検証用の本体ソース

    Public Shared Sub Main()

        Dim t As Test
        Dim ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(t))
        Marshal.StructureToPtr(t, ptr, False)
        For i As Integer = 0 To Marshal.SizeOf(t) - 1

            Marshal.WriteByte(ptr, i, CByte(i + 1))
        Next
        t = CType(Marshal.PtrToStructure(ptr, GetType(Test)), Test)
        Marshal.FreeHGlobal(ptr)

        Console.WriteLine("Test size {0}", Marshal.SizeOf(t))
        Console.WriteLine("A.b  size {0}", Marshal.SizeOf(t.x.b))
        Console.WriteLine("A.b   offset {0}", Marshal.OffsetOf(GetType(A), "b"))
        Console.WriteLine("A.i16 offset {0}", Marshal.OffsetOf(GetType(A), "i16"))
        Console.WriteLine("A.i8  offset {0}", Marshal.OffsetOf(GetType(A), "i8"))
        Console.WriteLine("A.i32 offset {0}", Marshal.OffsetOf(GetType(A), "i32"))

        Console.WriteLine("t.x.b   = {0:x}", t.x.b)
        Console.WriteLine("t.x.i16 = {0:x}", t.x.i16)
        Console.WriteLine("t.x.i8  = {0:x}", t.x.i8)
        Console.WriteLine("t.x.i32 = {0:x}", t.x.i32)
        'Console.ReadKey()
    End Sub