How to get a pointer to memory of Array instance?

Viewed 180

There are many examples over the net how to get pointer to byte[] or int[,], i.e. when you know exactly the element type and rank of the array. But how to get the pointer for generic Array?

The main problem here is I don't know rank in the advance. I don't know element type too but I know there can be only primitive numeric type (in my case), so I could type few extra ifs to cope with it.

Background: I would like to make this solution https://stackoverflow.com/a/52750659/6734314 more general -- in its current form it works only with double[,] so classic rank+type is given:

double[,] doubles =  {
     { 1, 2, 3, 4 },
     ...
};
fixed (double* p = doubles)
{
  ...

First attempt

From Olivier answer, all mistakes are mine.

Array data = new float[,] { { 4, 2 }, { 77, 3 } };
var reference = __makeref(data);
IntPtr pointer = **(IntPtr**)(&reference);

float* ptr = (float*)pointer.ToPointer();
{
  for (int i = 0; i < data.LongLength; ++i)
    Console.WriteLine(ptr[i]);
}
3 Answers

Combining:

Memory address of an object in C#

And

How can I display a pointer address in C#?

Writing:

Array array = Array.CreateInstance(typeof(int), 10);
unsafe
{
  var reference = __makeref(array);
  var pointer = **(IntPtr**)( &reference );
  Console.WriteLine("0x{0:x}", (ulong)&reference);
  Console.WriteLine("0x{0:x}", (long)&reference);
  Console.WriteLine("0x{0:x}", (ulong)pointer);
  Console.WriteLine("0x{0:x}", (long)pointer);
}

Outputs:

0x8cb87af070
0x8cb87af070
0x1a9c1c46290
0x1a9c1c46290

Source code of Array class

If the data is always the same shape (for example, 2D rectangular array), but potentially different types, then you can perhaps use the T : unmanaged constraint and the fixed keyword:

    static void Main()
    {
        // sample taken from comtrade91.pdf section 6.6
        var data = new float[,] { { 4, 2 }, { 77, 3 } };
        ShowAddressAndData(data);
    }
    static unsafe void ShowAddressAndData<T>(T[,] data) where T : unmanaged
    {
        fixed(T* ptr = data)
        {
            Console.WriteLine((IntPtr)ptr);
            for (int i = 0; i < data.Length; i++)
            {
                Console.WriteLine(ptr[i]);
            }
        }
    }
    // this extra method just to show that we can overload on dimension
    static unsafe void ShowAddressAndData<T>(T[] data) where T : unmanaged
    {
        fixed (T* ptr = data)
        {
            Console.WriteLine((IntPtr)ptr);
            //..
        }
    }

I think I found it, please comment if you found something too fragile/suspicious:

Array data = new float[,] { { 4, 2 }, { 77, 3 } };
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
  IntPtr address = handle.AddrOfPinnedObject();

  float* ptr = (float*)address.ToPointer();
  for (int i = 0; i < data.LongLength; ++i)
    Console.WriteLine(ptr[i]);
}
finally
{
  handle.Free();
}
Related