我无意中发现了一些对我没有意义的东西。我的问题在代码注释中以及下面:
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
[StructLayout(LayoutKind.Sequential, Size = 4096)]
public unsafe struct BufItems
{
public fixed byte Buffer[4096];
}
public class Wrapper
{
public BufItems Items;
public int Id;
}
private unsafe void button10_Click(object sender, EventArgs e)
{
Wrapper wrap = new Wrapper();
wrap.Id = 123;
fixed(BufItems* ptr = &wrap.Items)
{
ptr->Buffer[0] = 99; // works fine
}
// fixed (Wrapper w = wrap) { /* not possible */ };
// fixed (Wrapper* w = &wrap) { /* not possible */ };
// how come I can pin the object this way?
GCHandle h = GCHandle.Alloc(wrap, GCHandleType.Pinned);
// what exactly is p pointing to? Wrapper cannot have a pointer.
IntPtr p = h.AddrOfPinnedObject();
}
我遇到的另一个问题是:我假设字段BufItems Items
是作为对象创建的(因此是可固定的),而不是wrap
类对象实例化的一部分,对吧?否则,钉扎将无效,因为GC可以移动wrap
。然而,它是一个结构,我认为结构在这种情况下是“嵌入”的。这里到底发生了什么?
答案 0 :(得分:4)
让我们逐行解决您的问题:
fixed (Wrapper w = wrap) { /* not possible */ };
fixed
只允许声明一个指针变量。但请注意,引脚类型(fixed
语句的作用)可能在引用类型上有效,但不太有用,因此C#中没有任何内容可以使用它。
fixed (Wrapper* w = &wrap) { /* not possible */ };
Wrapper 是一种参考类型。允许您获取指向包含对它的引用的变量的指针,这反过来允许您访问对象的实际地址,并且可怕地搞乱它。您可以将指针转换为说出object*
,然后将任何对象存储在变量中,从而打破类型安全。
// how come I can pin the object this way?
GCHandle h = GCHandle.Alloc(wrap, GCHandleType.Pinned);
正如我已经说过的,实例固定在.NET中是可能的,但在C#中不是语法上的。您可以使用此方法固定任何blittable类型(不带引用字段)。固定对象可确保其在堆上的位置不会发生变化。
// what exactly is p pointing to? Wrapper cannot have a pointer.
IntPtr p = h.AddrOfPinnedObject();
也许最好在这段代码上说明一下:
int[] arr = new int[10];
fixed(int* p = arr) { ... }
fixed(int* p = &arr[0]) { ... }
这两行被编译为完全相同的CIL(性能),但第一行也可以通过使用 GCHandle 来实现。 AddrOfPinnedObject 返回指向对象中第一个字段的指针,与fixed(BufItems* ptr = &wrap.Items)
中的指针相同。
BufItems 是一个值类型,因此 Items 字段不包含引用,而是包含实际的固定数组以及跟随它的int。