fixed Statement (C# Reference)

The fixed statement prevents the garbage collector from relocating a movable variable. The fixed statement is only permitted in an unsafe context. fixed can also be used to create fixed size buffers.

The fixed statement sets a pointer to a managed variable and "pins" that variable during the execution of the statement. Pointers to movable managed variables are useful only in a fixed context. Without a fixed context, garbage collection could relocate the variables unpredictably. The C# compiler only lets you assign a pointer to a managed variable in a fixed statement.

class Point 
{ 
    public int x;
    public int y; 
}

unsafe private static void ModifyFixedStorage()
{
    // Variable pt is a managed variable, subject to garbage collection.
    Point pt = new Point();

    // Using fixed allows the address of pt members to be taken,
    // and "pins" pt so that it is not relocated.

    fixed (int* p = &pt.x)
    {
        *p = 1;
    }
}

You can initialize a pointer by using an array, a string, a fixed-size buffer, or the address of a variable. The following example illustrates the use of variable addresses, arrays, and strings. For more information about fixed-size buffers, see Fixed Size Buffers.

Point point = new Point();
double[] arr = { 0, 1.5, 2.3, 3.4, 4.0, 5.9 };
string str = "Hello World";

// The following two assignments are equivalent. Each assigns the address
// of the first element in array arr to pointer p.

// You can initialize a pointer by using an array.
fixed (double* p = arr) { /*...*/ }

// You can initialize a pointer by using the address of a variable. 
fixed (double* p = &arr[0]) { /*...*/ }

// The following assignment initializes p by using a string.
fixed (char* p = str) { /*...*/ }

// The following assignment is not valid, because str[0] is a char, 
// which is a value, not a variable.
//fixed (char* p = &str[0]) { /*...*/ } 

Starting with C# 7.3, the fixed statement operates on additional types beyond arrays, strings, fixed-size buffers, or unmanaged variables. Any type that implements a method named GetPinnableReference can be pinned. The GetPinnableReference must return a ref variable to an unmanaged type. See the topic on pointer types for more information. The .NET types System.Span<T> and System.ReadOnlySpan<T> introduced in .NET Core 2.0 make use of this pattern and can be pinned. This is shown in the following example:

unsafe private static void FixedSpanExample()
{
    int[] PascalsTriangle = {
                  1,
                1,  1,
              1,  2,  1,
            1,  3,  3,  1,
          1,  4,  6,  4,  1,
        1,  5,  10, 10, 5,  1
    };

    Span<int> RowFive = new Span<int>(PascalsTriangle, 10, 5);

    fixed (int* ptrToRow = RowFive)
    {
        // Sum the numbers 1,4,6,4,1
        var sum = 0;
        for (int i = 0; i < RowFive.Length; i++)
        {
            sum += *(ptrToRow + i);
        }
        Console.WriteLine(sum);
    }
}

If you are creating types that should participate in this pattern, see Span<T>.GetPinnableReference() for an example of implementing the pattern.

Multiple pointers can be initialized in one statement if they are all the same type:

fixed (byte* ps = srcarray, pd = dstarray) {...}

To initialize pointers of different types, simply nest fixed statements, as shown in the following example.

fixed (int* p1 = &point.x)
{
    fixed (double* p2 = &arr[5])
    {
        // Do something with p1 and p2.
    }
}

After the code in the statement is executed, any pinned variables are unpinned and subject to garbage collection. Therefore, do not point to those variables outside the fixed statement. The variables declared in the fixed statement are scoped to that statement, making this easier:

fixed (byte* ps = srcarray, pd = dstarray)
{
   ...
}
// ps and pd are no longer in scope here.

Pointers initialized in fixed statements are readonly variables. If you want to modify the pointer value, you must declare a second pointer variable, and modify that. The variable declared in the fixed statement cannot be modified:

fixed (byte* ps = srcarray, pd = dstarray)
{
    byte* pSourceCopy = ps;
    pSourceCopy++; // point to the next element.
    ps++; // invalid: cannot modify ps, as it is declared in the fixed statement.
}

In unsafe mode, you can allocate memory on the stack, where it is not subject to garbage collection and therefore does not need to be pinned. For more information, see stackalloc.

// Unsafe method: takes a pointer to an int.
unsafe static void SquarePtrParam(int* p)
{
    *p *= *p;
}

unsafe static void SquarePointValue()
{
    Point pt = new Point
    {
        x = 5,
        y = 6
    };
    // Pin pt in place:
    fixed (int* p = &pt.x)
    {
        SquarePtrParam(p);
    }
    // pt now unpinned.
    Console.WriteLine("{0} {1}", pt.x, pt.y);
    /*
    Output:
    25 6
     */
}

C# Language Specification

For more information, see the C# Language Specification. The language specification is the definitive source for C# syntax and usage.

See Also

C# Reference
C# Programming Guide
C# Keywords
unsafe
Fixed Size Buffers