1

I'm trying to complete the challenge of creating an XOR linked list. However, I can't finish it because every time I allocate a node object, it uses the same block of memory as the previous one.

var list = new ListXOR();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);

Console.WriteLine("Done.");

class ListXOR
{
    private readonly List<Node> _nodes = new();

    public void Add(int value)
    {
        Node node = new(value);
        _nodes.Add(node);
        unsafe
        {
            Console.WriteLine("Address of new node {0}", (ulong)&node);
        }
    }

    private class Node
    {
        public int value;

        public Node(int newValue) => value = newValue;
    }
}

This code displays in the console something like the following:

Address of new node 849654375800
Address of new node 849654375800
Address of new node 849654375800
Address of new node 849654375800
Done.

You also need to add this to your *.csproj file.

<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

Why does this happen? Is there a way to prevent it?

Jim G.
  • 15,141
  • 22
  • 103
  • 166
Jossean Yamil
  • 850
  • 2
  • 9
  • 22
  • 6
    You are not actually getting the memory address of the nodes. You are just getting the address of the `node` variable, which unsurprisingly are the same every time. See [this](https://stackoverflow.com/a/588837/5133585) for how to do this correctly. – Sweeper Jan 12 '23 at 01:50
  • Why do you care about memory addresses at all? You won't need them to build a linked list. Just use references. – Klaus Gütter Jan 12 '23 at 03:41
  • @KlausGütter, you do need them to create an XOR linked list so that you can run the XOR operator on the memory addresses. – Jossean Yamil Jan 12 '23 at 17:55

2 Answers2

2

The comments already discuss that the &node isn't doing what you want - it is returning the address of the local variable, not the object.

However: more importantly, you cannot create an XOR linked list with managed objects: you are required to only talk references, because the GC needs to be able to walk the managed heap to detect dead objects, and the GC reserves the right to move objects and fixup references. If you were able to obtain and store a XOR'd reference, you'll break the GC. You can obtain unsafe/unmanaged pointers to managed objects and values, but those pointers are only valid within a defined scope - either within a fixed block (which you can't do here), or via a GC "pin", and you don't want to allocate a GC pin for every node in a linked list.

The premise of what you are trying to do is flawed.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Yeah, didn't realize that `&node` was returning the address of the reference, not the object. I already knew your second point, that's why I added the nodes to a list to prevent the GC from collecting them. I said that it was only for a challenge, not that it was a useful functional list. – Jossean Yamil Jan 12 '23 at 17:56
  • @JosseanYamil having them in a list *isn't sufficient*; the GC can move managed objects at any point, as long as it observes certain rules - such as respecting `fixed` and "gc pin", and fixing up live references so that you never notice the move; if you somehow get a raw pointer (with or without xor), the GC **can and will make that pointer invalid** by moving things, and you'll find yourself getting access exceptions because you're trying to access invalidated pages - or worse: you *won't* get an access exception, and you'll read/write the wrong data – Marc Gravell Jan 12 '23 at 19:39
-4

To prevent this, you can create a new variable for each node object. For example, you could change the Add method to the following:

Node node = new Node(value);
    _nodes.Add(node);
    unsafe
    {
        Console.WriteLine("Address of new node {0}", (ulong)&node);
    }
Ece
  • 20
  • 3
  • That is what he is doing, he is just using the newer c# syntax that does not need the constructor name – Scott Chamberlain Jan 12 '23 at 01:45
  • It's still the same result. You can try it. But `Node node = new Node(value)` and `Node node = new(value)` is the same thing, it's just simply used to have a cleaner code. – Jossean Yamil Jan 12 '23 at 01:46