假设我有一个结构体。让我们也假装我们不知道它的类型。这就是为什么我们把它装箱而不能拆箱的原因。
public struct Player{
public float hp;
public float maxHP;
}
var boxedPlayer = (object)new Player();
var typeSize = typeof(Player);
var ourAllocatedMemoryPTR = (byte*)someAllocCall();
// Copy the object into the new memory
var objHandle = GCHandle.Alloc(boxedPlayer , GCHandleType.Pinned);
var adress = objHandle.AddrOfPinnedObject();
var ptr = adress.ToPointer();
Buffer.MemoryCopy(ptr, ourAllocatedMemoryPTR, typeSize, typeSize);
objHandle.Free();
据我所知,一个装箱的结构......与结构本身的大小不同......因为它被装箱和管理。所以在头部或尾部有一些字节将其定义为一个对象,一个装箱结构。所以上面的例子将整个盒装结构复制到内存中。我所知道的。这不是我们想要的。
是否可以只将框内的结构复制到分配的内存中?我们新分配的内存应该只存储结构,而不是装箱的。 我认为通过将结构从盒子中取出来可能有可能吗?在复制过程中切割将其定义为框的部分或头部/尾部?
这可能吗?结构体究竟是如何装箱的?在它之前和之后添加了多少字节?它在内存中看起来如何?
很高兴得到任何帮助!谢谢:)
答案 0 :(得分:3)
确切的内部布局可能会发生变化。 GCHandle.AddrOfPinnedObject() 之类的方法旨在为您提供指向对象数据的指针,而不是指向某些内部内容(如标题、方法表或填充字节)的指针。所以,就用那些方法吧,不要自己算。
<块引用>在固定句柄中检索对象数据的地址。
强调我的
在撰写本文时(以及几年前),内存中的 .NET 对象具有以下布局:
您可以通过以下代码看到这一点。我稍微简化了字段,以便我们可以比使用 float
更容易地看到模式。
public struct Player
{
public int hp ;
public int maxHP;
}
class Program
{
static unsafe void Main()
{
var player = new Player();
player.hp = 0xAABB;
player.maxHP = 0xCCDD;
var boxedPlayer = (object) player;
lock (boxedPlayer)
{
Console.ReadLine(); // Put a breakpoint here
}
}
}
在 32 位中,内存布局(Debug/Windows/Memory)是:
64 位内存布局:
Player
)hp
在这里maxHp
在这里如果你现在继续
var ourAllocatedMemoryPTR = (byte*) Marshal.AllocHGlobal(1024);
var objHandle = GCHandle.Alloc(boxedPlayer, GCHandleType.Pinned);
var adress = objHandle.AddrOfPinnedObject();
var ptr = adress.ToPointer();
Buffer.MemoryCopy(ptr, ourAllocatedMemoryPTR, sizeof(Player), sizeof(Player));
您会看到 ptr
指向 0x04a2a770,即 0x04ECA76C + 4(数据开始的地方)。 sizeof(Player)
为 8,表示两个 4 字节的 int
。
ourAllocatedMemoryPTR
前后 Buffer.MemoryCopy()
的记忆:
在像 WinDbg 这样的调试器中,32 位的结果如下:
0:009> dd 0486a76c-4 L4
0486a768 00000001 06d68ff8 0000aabb 0000ccdd
^Address ^Header ^MT ^hp ^maxHP
0:009> ? aabb
Evaluate expression: 43707 = 0000aabb
0:009> ? ccdd
Evaluate expression: 52445 = 0000ccdd
0:009> !do 0486a76c
Name: BoxedStructInMemory.Player
MethodTable: 06d68ff8
EEClass: 06d58ee4
Size: 16(0x10) bytes
File: C:\Users\...\BoxedStructInMemory.dll
Fields:
MT Field Offset Type VT Attr Value Name
0484697c 4000001 4 System.Int32 1 instance 43707 hp
0484697c 4000002 8 System.Int32 1 instance 52445 maxHP
ThinLock owner 1 (02810B18), Recursive 0
对于 64 位:
0:009> dq 000001a3c93ead50-8 L3
000001a3`c93ead48 00000001`00000000 00007ff8`a2f22180
000001a3`c93ead58 0000ccdd`0000aabb
0:009> !do 000001a3c93ead50
Name: BoxedStructInMemory.Player
MethodTable: 00007ff8a2f22180
EEClass: 00007ff8a2f1c5e8
Size: 24(0x18) bytes
File: C:\Users\...\BoxedStructInMemory.dll
Fields:
MT Field Offset Type VT Attr Value Name
00007ff8a2e2b1f0 4000001 8 System.Int32 1 instance 43707 hp
00007ff8a2e2b1f0 4000002 c System.Int32 1 instance 52445 maxHP
ThinLock owner 1 (000001A3C7899930), Recursive 0