Unsafe method to get pointer to byte array

2019-05-08 03:55发布

问题:

is this behaviour will be valid in C#

public class MyClass
{
    private byte[] data;
    public MyClass()
    {
        this.data = new byte[1024];
    }
    public unsafe byte* getData()
    {
        byte* result = null;
        fixed (byte* dataPtr = data)
        {
            result = dataPtr;
        }
        return result;
    }
}

回答1:

If you are going to turn off the safety system then you are responsible for ensuring the memory safety of the program. As soon as you do, you are required to do everything safely without the safety system helping you. That's what "unsafe" means.

As the C# specification clearly says:

the address of a moveable variable can only be obtained using a fixed statement, and that address remains valid only for the duration of that fixed statement.

You are obtaining the address of a moveable variable and then using it after the duration of the fixed statement, so the address is no longer valid. You are therefore specifically required to not do precisely what you are doing.

You should not write any unsafe code until you have a thorough and deep understanding of what the rules you must follow are. Start by reading all of chapter 18 of the specification.



回答2:

This code will compile just fine however it will lead to runtime issues. The code is essentially smuggling out a pointer to an unfixed object in the heap. The next GC which moves the MyClass type around will also move the data reference with it and any previously returned values from getData will now point to the incorrect location.

var obj = new MyClass();
unsafe byte* pValue = obj.getData();
// Assuming no GC has happened (bad assumption) then this works fine
*pValue = 42;

// Assume a GC has now happened and `obj` moved around in the heap.  The 
// following code is now over writing memory it simply doesn't own
*pValue = 42;

Did that last line cause the app to crash, overwrite a string value in another type or simply poke a value into an uninitialized array and just screw up a math problem else where? You have no idea. Best outcome is that the code just crashes quickly but in all likely hood it will do something far more subtle and evil.



回答3:

You could use the Marshal.StructureToPtr() method instead of unsafe magic :)

StructureToPtr copies the contents of structure to the pre-allocated block of memory that the ptr parameter points to.

Marshal.StructureToPtr Method (Object, IntPtr, Boolean)



回答4:

This code will not work (it will compile but at runtime it will cause problems). Once the fixed region ends, the data is no longer pinned.



回答5:

No, once you leave the fixed block, the value of result is no longer valid (it may coincidentally be valid if the GC hasn't run).

The proper way to do this kind of operation is to either have a reference to a byte[] in unmanaged memory that you access through C# code, or copying the managed array into unmanaged memory.