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;
}
}
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.
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.
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)
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.
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.