Reputation: 17362
There's some code I'm reading and part of it is:
public static unsafe byte[] GetBytes(uint value, bool BigEndian)
{
byte[] buff = new byte[4];
fixed (byte* pbyte = buff)*((uint*)pbyte) = value;
if (BigEndian)
buff.EndianConvert();
return buff;
}
I understand that it's just putting the four bytes at the location of the unit into a byte array, but I'm not clear on how.
My understanding of it is that:
(byte* pbyte = buff)
creates and returns a byte pointer pbyte, which points to the address of buff,
(uint*)pbyte
Casts the address of pbyte into a uint pointer?
I don't understand the rest of it though. What's the use of the fixed keyword? Why can't it be done like:
(byte* pbyte = buff) = (byte*)value;
Upvotes: 4
Views: 1085
Reputation: 361
During the CLR GC process, for normal GC heap objects (except LOH), the GC will mark and move the still alive objects to the next generation (this action also is named promoted). When promoting an object A, the GC will modify the object A address from addr-old to addr-new, and then update the relationships of all the objects who referenced to this objects.
For example, object A is referenced by object B and object C. This means object B have a pointer which is pointed to object A, and object C also have a pointer which is pointed to object A. Then, in the promoting phase, address of object A will be changed from addr-old to addr-new, then, GC will also modify the reference pointer value of object B and object C. After the modification, object B and C have the correct pointers which still pointed to A now.
After the line "byte* pbyte = buff" executed, pbyte has a pointer pointed to object "buff" , let's say the pbyte address is 0x12345678 (it also means buff address is 0x12345678). And now, GC happened, and the object "buff" will be promoted to a new generation, which means object "buff" will have a new memory address, for example, it's "0x55555555". But, "pbyte" is a native (unmanaged) object, and the CLR does not know how to maintain its lifecycle, so, although pbyte have a relationship with buff, but the CLR cannot change the pbyte address from 0x12345678 to 0x55555555, the pointer "pbyte" still points to address 0x12345678, but this address does not belong to object "buff"; the pointer "pbyte" is a bad pointer now.
The "fixed" statement will ensure the managed object "buff" will not be promoted. It also also means it will not be moved from here to there.
Upvotes: 0
Reputation: 7259
The fixed statement prevents the garbage collector from relocating a movable variable. If you omit it, your address could get changed and you'll get an error.
Upvotes: 2
Reputation: 124702
Others have explained the concept of pinning, but I think what has confused you is the fact that it is all on one line.
fixed (byte* pbyte = buff)*((uint*)pbyte) = value;
Is equivalent to:
fixed (byte* pbyte = buff)
{
*((uint*)pbyte) = value;
}
Just as this:
if(someCondition) DoSomething();
Is equivalent to:
if(someCondition)
{
DoSomething();
}
Does that clear it up? It should now be obvious that the first part is the declaration of the variable and the associated block, and the second part is the assignment.
Upvotes: 2
Reputation: 7959
You can't just take the address of an array because it's managed by the garbage collector, meaning that its location in memory can change at any time. The fixed keyword pins the array in place for the duration of its scope, allowing the four bytes of the array to be filled by a (4-byte) uint value.
Upvotes: 1