fixed statement - pin a variable for pointer operations
The fixed
statement prevents the garbage collector from relocating a moveable variable and declares a pointer to that variable. The address of a fixed, or pinned, variable doesn't change during execution of the statement. You can use the declared pointer only inside the corresponding fixed
statement. The declared pointer is readonly and can't be modified:
unsafe
{
byte[] bytes = [1, 2, 3];
fixed (byte* pointerToFirst = bytes)
{
Console.WriteLine($"The address of the first array element: {(long)pointerToFirst:X}.");
Console.WriteLine($"The value of the first array element: {*pointerToFirst}.");
}
}
// Output is similar to:
// The address of the first array element: 2173F80B5C8.
// The value of the first array element: 1.
Note
You can use the fixed
statement only in an unsafe context. The code that contains unsafe blocks must be compiled with the AllowUnsafeBlocks compiler option.
You can initialize the declared pointer as follows:
With an array, as the example at the beginning of this article shows. The initialized pointer contains the address of the first array element.
With an address of a variable. Use the address-of
&
operator, as the following example shows:unsafe { int[] numbers = [10, 20, 30]; fixed (int* toFirst = &numbers[0], toLast = &numbers[^1]) { Console.WriteLine(toLast - toFirst); // output: 2 } }
Object fields are another example of moveable variables that can be pinned.
When the initialized pointer contains the address of an object field or an array element, the
fixed
statement guarantees that the garbage collector doesn't relocate or dispose of the containing object instance during the execution of the statement body.With the instance of the type that implements a method named
GetPinnableReference
. That method must return aref
variable of an unmanaged type. The .NET types System.Span<T> and System.ReadOnlySpan<T> make use of this pattern. You can pin span instances, as the following example shows:unsafe { int[] numbers = [10, 20, 30, 40, 50]; Span<int> interior = numbers.AsSpan()[1..^1]; fixed (int* p = interior) { for (int i = 0; i < interior.Length; i++) { Console.Write(p[i]); } // output: 203040 } }
For more information, see the Span<T>.GetPinnableReference() API reference.
With a string, as the following example shows:
unsafe { var message = "Hello!"; fixed (char* p = message) { Console.WriteLine(*p); // output: H } }
With a fixed-size buffer.
You can allocate memory on the stack, where it's not subject to garbage collection and therefore doesn't need to be pinned. To do that, use a stackalloc
expression.
You can also use the fixed
keyword to declare a fixed-size buffer.
C# language specification
For more information, see the following sections of the C# language specification: