Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A pointer type in C# is a variable that stores the direct memory address of another variable, rather than its value or a managed reference. Pointers bypass C#‘s default memory safety mechanisms and garbage collection tracking, requiring explicit developer management within an unsafe execution context.

Syntax and Declaration

A pointer is declared by appending an asterisk (*) to the underlying type.
type* identifier;
Multiple pointers can be declared in a single statement. Unlike C/C++, the asterisk is bound to the underlying type, not the variable name.
int* p1, p2, p3; // p1, p2, and p3 are all pointers to integers
void* pVoid;     // Pointer to an unknown type

The unsafe Context

Because pointers manipulate raw memory, the C# compiler requires them to be scoped within an unsafe context. This can be applied as a modifier to a method, class, or struct, or used as a block statement. The project must also be compiled with the AllowUnsafeBlocks flag.
unsafe void ManipulateMemory()
{
    int value = 10;
    int* ptr = &value;
}

// OR

void ManipulateMemoryBlock()
{
    unsafe
    {
        int value = 10;
        int* ptr = &value;
    }
}

Pointer Operators

C# provides specific operators for pointer manipulation and memory traversal:
  • & (Address-of): Returns the memory address of a variable.
  • * (Indirection/Dereference): Accesses the data located at the memory address the pointer holds.
  • -> (Member Access): Accesses a member of a struct through a pointer.
  • ++, --, +, - (Pointer Arithmetic): Increments or decrements the memory address based on the sizeof the underlying type.
unsafe
{
    int number = 42;
    int* pNumber = &number; // Address-of
    
    *pNumber = 100;         // Dereference and assign
    
    pNumber++;              // Moves the pointer forward by sizeof(int) (4 bytes)
}

Type Constraints (Unmanaged Types)

Pointers can only point to unmanaged types. An unmanaged type is any type that is not a reference type and does not contain reference type fields at any level of nesting. Valid unmanaged types include:
  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Any enum type.
  • Any other pointer type.
  • Any user-defined struct that contains fields of unmanaged types only.
Pointers cannot point to classes, arrays, strings, or interfaces, because the .NET Garbage Collector (GC) is free to relocate reference types in memory during heap compaction, which would render the pointer’s memory address invalid.

Memory Pinning (fixed Statement)

When a pointer needs to reference an unmanaged field encapsulated within a managed reference type (like an array or a class), the managed object must be pinned in memory. The fixed statement prevents the Garbage Collector from relocating the object while the pointer is in use.
class Container
{
    public int Value;
}

unsafe void PinMemory()
{
    Container obj = new Container();

    // Pin 'obj' so the GC does not move it while we hold a pointer to 'Value'
    fixed (int* pValue = &obj.Value)
    {
        *pValue = 99;
    }
}

Pointer Conversions

C# enforces strict type safety, even within unsafe contexts, but allows explicit casting between pointer types.
  • Implicit Conversions: Any pointer type can be implicitly converted to a void*. The null literal can be implicitly converted to any pointer type.
  • Explicit Conversions: Casting between different pointer types (e.g., int* to byte*), or between a pointer type and an integral type (e.g., int* to long), requires an explicit cast.
unsafe
{
    int number = 1024;
    int* pInt = &number;
    
    // Implicit conversion to void*
    void* pVoid = pInt;
    
    // Explicit conversion from void* to byte*
    byte* pByte = (byte*)pVoid;
    
    // Explicit conversion from pointer to integral type
    long address = (long)pInt;
}
Note: A void* pointer cannot be dereferenced or subjected to pointer arithmetic because the compiler does not know the size of the underlying type.
Master C# with Deep Grasping Methodology!Learn More