What's the use of C# keyword fixed/unsafe?
For example, C# fixed Keyword (unsafe)
using System;
class Program
{
unsafe static void Main()
{
fixed (char* value = "sam")
{
char* ptr = value;
while (*ptr != '\0')
{
Console.WriteLine(*ptr);
++ptr;
}
}
}
}
Why do I need to fix it in the first place?
C# is a managed language that means the memory is managed automatically, i.e. not by you. If you did not use fixed
by the time you come to modify the memory pointed to by your pointer C# could have moved the variable to another memory location so you could be modifying something else!
fixed
is logically fixing the variable in memory so it does not move around.
Why does C# move variables in memory around? To compact the memory otherwise programs would use up more memory available to them if objects that are no longer alive left holes other objects cannot fit in (heap memory fragmentation).
I used fixed
extensively in a .NET library designed for resource constrained devices to avoid creating garbage copying into buffers and find this feature sorely lacking in other managed languages where you cannot do the same. When writing games in a managed language garbage collection is often one of the biggest bottlenecks so having the ability not to create it is very helpful!
See my question here: C# Copy variables into buffer without creating garbage? for one reason why.
unsafe
is necessary to deal in pointers.
fixed
has two uses:
unsafe struct
field, it declares a "fixed buffer" - a reserved block of space in a type that is accessed via pointers rather than regular fieldsTo answer with a specific example - here's some code that is used to perform semantic equality between two byte[]
of arbitrary size...
internal static unsafe int GetHashCode(byte[] value)
{
unchecked
{
if (value == null) return -1;
int len = value.Length;
if (len == 0) return 0;
int octects = len / 8, spare = len % 8;
int acc = 728271210;
fixed (byte* ptr8 = value)
{
long* ptr64 = (long*)ptr8;
for (int i = 0; i < octects; i++)
{
long val = ptr64[i];
int valHash = (((int)val) ^ ((int)(val >> 32)));
acc = (((acc << 5) + acc) ^ valHash);
}
int offset = len - spare;
while(spare-- != 0)
{
acc = (((acc << 5) + acc) ^ ptr8[offset++]);
}
}
return acc;
}
}
So if, for example, the buffer was 1000 items, by treating it as a set of long
we now only do 125 iterations rather than having to look individually at all 1000 - plus we completely bypass any array bounds checking (which the JIT may or may not remove, depending on how obvious it looks that you can't possibly be violating them).
Unsafe
code blocks are quite rare to be used. They are predominantly used if you want to create and make use of pointers. Also, any code in unsafe block is out of control of CLR. It is considered to be unverifiable (from MSDN) as far as CLR in concerned.
Now, when garbage collection happens, some object may well be relocated. When prefixed with fixed, we are telling the framework to not to relocate objects whose address pointers are pointing to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With