Unsafe Code and Pointers
C# supports direct memory manipulation via pointers within blocks of code marked unsafe and compiled with the /unsafe compiler option.
Pointer Basics
A pointer instance holds the address of a variable. Pointer types can be (unsafely) cast to any other pointer type.
| & | The address-of operator returns a pointer to the address of a variable |
| * | The dereference operator returns the variable at the address of a pointer |
| -> | The pointer-to-member operator is a syntactic shortcut, in which x->y is equivalent to (*x).y |
Unsafe Code
Using the keyword unsafe for a type, type member or statement block, you are permitted to use pointer types and perform C++ style pointer operations on memory within that scope.
// C# supports direct memory manipulation via pointers within blocks of code marked unsafe
// and compiled with the /unsafe compiler option. LINQPad implicitly compiles with this option.
// Here's how to use pointers to quickly process a bitmap:
unsafe static void BlueFilter (int[,] bitmap)
{
int length = bitmap.Length;
fixed (int* b = bitmap)
{
int* p = b;
for (int i = 0; i < length; i++)
*p++ &= 0xFF;
}
}
static void Main()
{
int[,] bitmap = { {0x101010, 0x808080, 0xFFFFFF}, {0x101010, 0x808080, 0xFFFFFF} };
BlueFilter (bitmap);
bitmap.Dump();
}
The Fixed Statement
The fixed statement is required to pin a managed object so that the garbage collector can pin the object and not move it around.
Within a fixed statement, you can get a pointer to any value type, an array of value types, or a string. In the case of arrays and strings, the pointer will actually point to the first element, which is a value type.
// Value types declared inline within reference types require the reference type to be pinned:
class Test
{
public int X;
}
static void Main()
{
Test test = new Test();
unsafe
{
fixed (int* p = &test.X) // Pins test
{
*p = 9;
}
Console.WriteLine (test.X);
}
}
The Pointer-to-Member Operator
// In addition to the & and * operators, C# also provides the C++ style -> operator,
// which can be used on structs:
struct Test
{
public int X;
}
unsafe static void Main()
{
Test test = new Test();
Test* p = &test;
p->X = 9;
Console.WriteLine (test.X);
}
Arrays
The stackalloc keyword
// Memory can be allocated in a block on the stack explicitly using the stackalloc keyword:
unsafe
{
int* a = stackalloc int [10];
for (int i = 0; i < 10; ++i)
Console.WriteLine (a[i]); // Print raw memory
}
Fixed-size buffers
// Memory can be allocated in a block within a struct using the fixed keyword:
unsafe struct UnsafeUnicodeString
{
public short Length;
public fixed byte Buffer[30];
}
unsafe class UnsafeClass
{
UnsafeUnicodeString uus;
public UnsafeClass (string s)
{
uus.Length = (short)s.Length;
fixed (byte* p = uus.Buffer)
for (int i = 0; i < s.Length; i++)
p[i] = (byte) s[i];
}
}
static void Main() { new UnsafeClass ("Christian Troy"); }
void*
// A void pointer (void*) makes no assumptions about the type of the underlying data and is
// useful for functions that deal with raw memory:
//A void* cannot be dereferenced, and arithmetic operations cannot be performed on void pointers.
unsafe static void Main()
{
short[] a = {1,1,2,3,5,8,13,21,34,55};
fixed (short* p = a)
{
//sizeof returns size of value-type in bytes
Zap (p, a.Length * sizeof (short));
}
foreach (short x in a)
System.Console.WriteLine (x); // Prints all zeros
}
unsafe static void Zap (void* memory, int byteCount)
{
byte* b = (byte*) memory;
for (int i = 0; i < byteCount; i++)
*b++ = 0;
}