.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