1

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]);
}
astrowalker
  • 3,123
  • 3
  • 21
  • 40
  • So, the pointer from the array is always the address of the first element, can you get it? and if you don't know anything about the array how you get the array, as object or something else? – spzvtbg Sep 15 '20 at 10:30
  • 1
    Note: most times that you've obtained a pointer in such ways, you need to be **really, really careful** - that pointer can't really be *used* for much, because as soon as it is an unmanaged pointer (rather than a managed pointer), outside of a "fixed" region or a pin: **you cannot trust the object to still be there** (it can move at any time) – Marc Gravell Sep 15 '20 at 11:29
  • @MarcGravell, yes, thank you, that was my thought, finally I found all the pieces (see my post-answer), I use pinned memory now when reading it. – astrowalker Sep 15 '20 at 11:58

3 Answers3

2

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

  • Thank you very much +1, but I don't see how it works -- see my updated question, I should see the values, but I don't. Either memory was moved, or the pointer shows some other place, and not data memory of the array. – astrowalker Sep 15 '20 at 11:18
2

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);
            //..
        }
    }
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

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();
}
astrowalker
  • 3,123
  • 3
  • 21
  • 40
  • looks great to me; question, though: are the arrays at least predictable in *shape*, if not in type? there's also possibly some things you can do with generics, if they are always (say) `T[,]` (2d array for some type `T`). Note: you should *probably* use `try`/`finally` here so that the handle is always free'd, even in the failure case – Marc Gravell Sep 15 '20 at 12:22
  • @MarcGravell, good point about finally, no sense going public with bad habits. In my case shape (rank) can be anything, type can be one of basic numeric types -- byte, sbyte, int, etc. – astrowalker Sep 15 '20 at 12:35