C#:将指针类型用作字段?

时间:2009-11-09 21:09:13

标签: c# pointers unsafe

在C#中,可以声明一个具有指针类型成员的结构(或类),如下所示:

unsafe struct Node
{
  public Node* NextNode;
}

使用这种结构是否安全(错误......暂时忽略具有讽刺性的小unsafe旗...)?我的意思是在堆上长期存储。根据我的理解,GC可以自由移动,当它更新对已被移动的内容的引用时,它是否也更新了指针?我猜不是,这会使这种结构非常不安全,对吗?

我确信这样做有更好的选择,但称之为病态的好奇心。

编辑:似乎有些混乱。我知道这不是一个很好的结构,我纯粹想知道这是否是一个安全的结构,即:指针是否保证指向你最初指向它的东西?

原始C代码用于遍历树(深度优先)而不进行递归,其中树存储在数组中。然后通过递增指针遍历数组,除非满足某个条件,然后将指针设置为NextNode,其中遍历继续。当然,在C#中也可以通过以下方式完成:

struct Node
{
  public int NextNode;
  ... // other fields
}

int是下一个节点数组中的索引。但是出于性能原因,我最终会使用指针和fixed数组进行调整以避免边界检查,并且原始的C代码看起来更自然。

5 个答案:

答案 0 :(得分:12)

  

使用这种结构是否安全?我的意思是在堆上长期存储。

是。这样做通常是愚蠢,痛苦和不必要的,但这是可能的。

  

根据我的理解,GC可以随意移动,当它更新对已被移动的内容的引用时,它是否也会更新指针?

没有。 这就是我们让您将其标记为不安全的原因。

  

我猜不,这会使这种结构非常不安全,对吗?

正确。

  

我确信这样做有更好的选择,但称之为病态的好奇心。

肯定有。

  

是保证指向你最初指向它的指针的指针吗?

除非您确保发生这种情况。有两种方法可以做到这一点。

方法一:告诉垃圾收集器不要移动内存。有两种方法可以做到这一点:

  • 使用“fixed”语句修复变量。

  • 使用互操作服务在一个地方为您希望保持活动的结构创建一个gc句柄。

执行上述任何一项操作都很可能会破坏垃圾收集器的性能。

方式二:不要引用垃圾收集器可能移动的内存。有两种方法可以做到这一点:

  • 仅获取局部变量,值参数或堆栈分配块的地址。当然,在这样做时,您需要确保指针的存活时间不会超过相关的堆栈帧,否则,您将引用垃圾。

  • 从非托管堆中分配一个块,然后在该块内使用指针。在本质上,实现自己的内存管理器。您需要正确实现新的自定义内存管理器。小心。

答案 1 :(得分:2)

为什么不:

struct Node
{
    public Node NextNode;
}

或至少:

struct Node
{
    public IntPtr NextNode;
}

您可以使用fixed语句来阻止GC移动指针。

答案 2 :(得分:2)

是的,垃圾收集器可以移动对象,不,它不会更新你的指针。您需要修复指向的对象。有关详细信息,请参阅此memory management explanation

你可以修复这样的对象:

  unsafe {
     fixed (byte* pPtr = object) {
         // This will fix object in the memory
        }
     }
  }

指针的优点通常是性能和与其他不安全代码的交互。没有越界检查等,加速你的代码。但就像你在编程中一样C你必须非常小心你正在做的事情。

答案 3 :(得分:2)

已排除一些明显的完整性检查。显而易见的问题是,您必须分配超出您需要的数量,因为您无法重新分配缓冲区,因为关键字已修复意味着。

public unsafe class NodeList
{
    fixed Node _Nodes[1024];
    Node* _Current;

    public NodeList(params String[] data)
    {
        for (int i = 0; i < data.Length; i++)
        {
            _Nodes[i].Data = data[i];
            _Nodes[i].Next = (i < data.Length ? &_Nodes[i + 1] : null);     
        }

        _Current = &_Nodes[0];
    }

    public Node* Current()
    {
        return _Current++;  
    }
}

public unsafe struct Node
{
    public String Data;
    public Node* Next;
}

答案 4 :(得分:2)

一个危险的想法,但它可能有效:

当你的结构数组超过一定的大小(85000字节)时,它将被分配到Large Object Heap,在那里扫描和收集块但不移动...

链接文章指出了较新的CLR版本可能会在LOH上移动内容的危险......