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

Leave a Reply

Your email address will not be published. Required fields are marked *