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