【问题标题】:XOR linked list异或链表
【发布时间】:2011-05-12 21:06:59
【问题描述】:

我最近发现下面的链接很有趣。

http://en.wikipedia.org/wiki/XOR_linked_list

  • 通用调试工具 不能遵循 XOR 链,使得 调试比较困难; [1]
  • 内存减少的代价 使用是代码的增加 复杂性,使维护更多 昂贵;
  • 大多数垃圾收集方案都可以 不适用于执行以下操作的数据结构 不包含文字指针;
  • 中未定义指针的异或 一些上下文(例如,C 语言), 尽管许多语言提供了一些 之间的类型转换 指针和整数;
  • 如果出现以下情况,指针将不可读 一个没有遍历列表——因为 例如,如果指向列表的指针 项目包含在另一个数据中 结构;
  • 在遍历列表时,您需要 记住对方的地址 以前访问过的节点,以便 计算下一个节点的地址。

现在我想知道这是否是低级语言独有的,还是在 C# 中也可以?

是否有任何类似的选项可以使用 C# 产生相同的结果?

【问题讨论】:

    标签: c# linked-list unsafe xor xor-linkedlist


    【解决方案1】:

    TL;DR我很快用 C# 写了一个概念验证 XorLinkedList implementation

    在 C# 中使用 unsafe 代码是绝对可能的。不过有一些限制:

    1. XorLinkedList 必须是“非托管结构”,即它们不能包含托管引用
    2. 由于 C# 泛型的限制,链表不能是泛型的(即使是 where T : struct 也不行)

    后者似乎是因为您不能将泛型参数限制为非托管结构。仅使用where T : struct,您还可以允许包含托管引用的结构。

    这意味着您的 XorLinkedList 只能保存原始值,如整数、指针或其他非托管结构。

    C# 中的低级编程

    private static Node* _ptrXor(Node* a, Node* b)
    {
        return (Node*)((ulong)a ^ (ulong)b);//very fragile
    }
    

    非常脆弱,我知道。 C# 指针和 IntPtr 不支持 XOR 运算符(可能是个好主意)。

    private static Node* _allocate(Node* link, int value = 0)
    {
        var node = (Node*) Marshal.AllocHGlobal(sizeof (Node));
        node->xorLink = link;
        node->value = value;
        return node;
    }
    

    之后不要忘记Marshal.FreeHGlobal 这些节点(实现完整的IDisposable 模式,并确保将免费调用放在if(disposing) 块之外。

    private static Node* _insertMiddle(Node* first, Node* second, int value)
    {
        var node = _allocate(_ptrXor(first, second), value);
        var prev = _prev(first, second);
        first->xorLink = _ptrXor(prev, node);
        var next = _next(first, second);
        second->xorLink = _ptrXor(node, next);
        return node;
    }
    

    结论

    就我个人而言,我永远不会在 C# 中使用 XorLinkedList(当我编写内存分配器或内核数据结构等非常低级的系统内容时,也许在 C 中。在任何其他设置中,存储效率的小幅提升确实不值得痛苦。你不能将它与 C# 中的托管对象一起使用,这使得它对于日常编程几乎毫无用处。

    今天,存储几乎是免费的,即使是主内存,如果您使用 C#,您可能不太关心存储。我在某处读到 CLR 对象标头约为 40 字节,所以这个指针将是您最不关心的问题;)

    【讨论】:

    • 值得注意的是,这不应与 C# 中的 ^(托管堆上的对象句柄)运算符混淆。
    【解决方案2】:

    C# 通常不允许您在该级别操作引用,所以很遗憾。

    【讨论】:

    • 这并不完全正确。指针结构(包括 XOR 链表)可以在托管代码中通过使用数组和实际上是数组索引的假指针来实现。
    • 不知道你为什么认为这很不幸!
    • @Robin Green - 能够模拟一件事与能够做一件事不同。
    • 那里有一场有趣的哲学辩论
    【解决方案3】:

    作为已提出的不安全解决方案的替代方案。

    如果您使用数组或列表集合支持您的链表,而不是内存指针“next”和“previous”指示数组的索引,您可以实现此异或,而无需使用不安全的功能。

    【讨论】:

      【解决方案4】:

      在 C# 中有一些方法可以使用指针,但您只能临时拥有指向对象的指针,因此您不能在这种情况下使用它们。造成这种情况的主要原因是垃圾收集——只要你能做一些异或指针之类的事情,然后再对它们进行非异或,GC 就无法知道收集某个对象是否安全。

      你可以通过在一个大数组中使用索引来模拟指针来做一些非常相似的事情,但是你必须自己实现一种简单的内存管理形式(即,当创建新节点时,我应该把它放在数组的哪个位置?)。

      另一种选择是使用 C++/CLI,它一方面让您可以充分利用指针的灵活性,另一方面可以在需要时访问框架。

      【讨论】:

        【解决方案5】:

        当然。你只需要编写类。 c# 中的 XOR 运算符是 ^ 这应该是您开始编码所需的全部内容。 请注意,这将要求将代码声明为“不安全”。请参阅here: 了解如何在 c# 中使用指针。

        【讨论】:

        • 我认为在这种情况下没有任何“只需要......”。您将不得不处理不安全的代码和指针,C# 将与您争夺这两者。
        • 抱歉,正在编辑您评论的帖子。是的,它不是那么简单,但它肯定是可能的。
        • 考虑到你将不得不欺骗编译器让你使用指向托管类型的指针,我怀疑这是否可行,更不用说在任何级别上都可用。
        • 为什么会涉及到欺骗?我同意,编写不安全的代码通常不是一个好主意,但要获得指向结构的指针需要 mystruct*,这似乎并不难。
        • 您只能在 C# 中临时获取指针,使用 fixedstackalloc。只要当前方法结束,您固定的对象就可以移动,并且很可能会被垃圾回收。
        【解决方案6】:

        在这里做一个广泛的概括:C# 似乎走的是可读性和干净接口的道路,而不是位摆弄和尽可能密集地打包所有信息的道路。

        因此,除非您在此处有特殊需要,否则应使用提供的列表。未来的维护程序员会感谢你的。

        【讨论】:

          【解决方案7】:

          有可能,但是您必须了解 C# 如何看待对象。实例变量实际上并不包含对象,而是指向内存中对象的指针。

          DateTime dt = DateTime.Now;
          

          dt 是指向内存中包含 DateTime 方案的结构的指针。

          所以你可以做这种类型的链表,虽然我不确定你为什么会这样做,因为框架通常已经实现了最有效的集合。作为一种思想实验,这是可能的。

          【讨论】:

          • 不好的例子 - DateTimes 是结构,因此是值类型,而不是引用类型。
          猜你喜欢
          • 1970-01-01
          • 2012-06-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-08-09
          • 1970-01-01
          • 1970-01-01
          • 2013-03-09
          相关资源
          最近更新 更多