Search code examples
c#pointersstructunsafe

Unsafe structs in C#


I wanted to try c#'s unsafe 'feature' by creating simple structs (Vector, Particle).

SITUATION

I have this 2 structs and want to inject position and velocity vectors into my Particle struct. As a test I wanted to print out position's X value, but somehow I'm getting random values.

I have the following code here

Vector

public readonly struct Vector
{
    public int X { get; }
    public int Y { get; }

    public Vector(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Particle

public unsafe struct Particle
{
    private Vector* mPosition;
    private Vector* mVelocity;

    public Particle(Vector position, Vector velocity = default)
    {
        mPosition = &position; // here is x 10
        mVelocity = &velocity;
    }

    public int GetPosX()
    {
        return mPosition->X; // but here not
    }
}

Program

public class Program
{
    private static void Main(string[] args)
    {
        var pos = new Vector(10, 5);
        var obj = new Particle(pos);

        Console.WriteLine(obj.GetPosX()); // prints random value
    }
}

PROBLEM

It prints a random value instead of 10.


Solution

  • class Program {
        static void Main (string [ ] args) {
            unsafe {
                Vector pos = new Vector(10, 5);
                Particle obj = new Particle(&pos);
                // &pos is at position 0xabcdef00 here.
                // obj.mPosition has a different value here. It points to a different address? Or am I misunderstanding something
                Console.WriteLine(obj.GetPosX( ));
            }
        }
    }
    
    public struct Vector {
        public int X;
        public int Y;
    
        public Vector (int x, int y) {
            X = x;
            Y = y;
        }
    }
    
    public unsafe struct Particle {
        private Vector* mPosition;
    
        public Particle (Vector *position) {
            mPosition = position; // here is x 10
        }
    
        public int GetPosX ( ) {
            return mPosition->X; // still 10 here
        }
    }
    

    This works for me. Please ... do not ask me why it does. You will notice that I didn't change that much. Just calling Particle with *pos instead of pos. For some reason that fixes the problem. You have to wrap the code with unsafe then and change the constructor for Particle obviously.

    I could speculate about why it works, but I'd rather not. Maybe the pointer changes when you pass pos as a parameter for some reason?