4

Why can't I cast an array of arrays in C# to a pointer to pointer?

public int WriteAudio(short[][] audio, uint num_channels, uint channel_len)
{
    int ret = 0;
    unsafe
    {
        fixed (short** d = audio)  // Compiler complains about this
        {
            ret = MyCppDLL.WriteDeinterlacedAudio(d, num_channels, channel_len);
        }
    }
    return ret;
}

I know the following is possible:

public int WriteAudio(short[] audio, uint num_channels, uint channel_len)
{
    int ret = 0;
    unsafe
    {
        fixed (short* d = audio) // No complaints here
        {
            ret = MyCppDLL.WriteInterlacedAudio(d, num_channels, channel_len);
        }
    }
    return ret;
}

Thanks!

5
  • Why can't you use a Collection or a multi-dimensional array? Commented Dec 22, 2015 at 23:22
  • The function: MyCppDLL.WriteDeinterlacedAudio() is in a C++ wrapper DLL. I don't think a Collection or multi-dimensional array can be cast either, can it? Commented Dec 22, 2015 at 23:48
  • I think you can only have an array of pointers.. not a pointers of pointers... see this if it helps: tutorialspoint.com/cprogramming/c_array_of_pointers.htm Commented Dec 23, 2015 at 0:23
  • I suspect it's because .NET arrays are more than just data, but also contains metadata and padding. (e.g., Length). When you cast an array to a pointer, you're just pointing to the data. But you can't quite do the same with array of arrays since you have to get around the metadata of each inner array. I'm afraid you'll need to do a conversion somewhere. Commented Dec 23, 2015 at 1:43
  • This question may be relevant: stackoverflow.com/questions/890098/…. Commented Dec 23, 2015 at 1:55

2 Answers 2

4

To use the double pointer, you can obtain a fixed pointer to each array element, place each element into a new array of double*s, and take a fixed pointer to that array to get the desired double**:

GCHandle[] handles = null;
try
{
    handles = audio
        .Select((z, j) => GCHandle.Alloc(audio[j], GCHandleType.Pinned))
        .ToArray();

    double*[] audioPtr = new double*[audio.Length];
    for (int j = 0; j < audioPtr.Length; j++)
        audioPtr[j] = (double*)handles[j].AddrOfPinnedObject();

    fixed (double** z = audioPtr)
    {
        // call extern method here
    }
}
finally
{   // unpin the memory
    foreach (var h in handles)
    {
        if (h.IsAllocated) h.Free();
    }
}

Note that the accepted answer at Converting from a jagged array to double pointer in C# presents a similar situation, but the accepted answer is incorrect. Another answer to this question uses the same incorrect methodology:

for (int i = 0; i < array.Length; i++)
    fixed (double* ptr = &array[i][0])
    {
        arrayofptr[i] = ptr;
    }

fixed (double** ptrptr = &arrayofptr[0])
{
    //whatever
}

Do not do this! The pointer *ptr is set in a fixed context, but is used outside the fixed context as an address element in arrayofptr. After the fixed block completes, the GC is free to reallocate the memory, at which point *ptr may point to a different object. The answer above prevents this possibility by using GCHandle to allocate a fixed pointer, ensuring that the individual elements in audio will not be relocated in memory until after the native call completes.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for this answer! It looks good. It's vacation time now, but I'll try it out as soon as I can and at that point mark the answer as accepted.
2

The function: MyCppDLL.WriteDeinterlacedAudio() is in a C++ wrapper DLL. I don't think a Collection or multi-dimensional array can be cast either, can it?

It can, but need to take some pains.

Because short[][] is an array of arrays and since unsafe code supports at most 1 "conversion" from array-pointer at a time in C#, the best you can do it to convert it to array of pointers first before converting it to pointer of pointers

in short, you need to do: array of arrays -> array of pointers -> pointer of pointers

Edit: I decided to put this note after comment by Mr. drf, please refer to his explanation and code. The idea above is to convert array of arrays to array of pointers before converting to pointer to pointers can still be used though. But how to do it safely, please refer to his more complete answer.

Something like this:

public int WriteAudio(short[][] audio, uint num_channels, uint channel_len) {
    int ret = 0;
    unsafe {
            fixed (short* d = &audio[0][0])  //pointer to the address of first element in audio
            {
                short*[] arrOfShortPtr = new short*[audio.Length]; //here is the key!! really, this is now array of pointers!
                for (int i = 0; i < arrOfShortPtr.Length; ++i) {
                    fixed (short* ptr = &audio[i][0]) {
                        arrOfShortPtr[i] = ptr; //like magic, casting from array of arrays to array of pointers
                    }
                }
                fixed (short** ptrOfPtr = &arrOfShortPtr[0]) { //now it is fine... second casting from array of pointers to pointer of pointers
                    //do what you want
                }
            }
        }
        return ret;
    }

2 Comments

This answer contains an error. After the for loop completes, arrOfShortPtr contains pointers to non-fixed memory, and the GC can reallocate this memory at any point.
I updated my answer and refer the way to do it safely to your code and explanation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.