How to Resolve Error "CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer" in C#
The Compiler Error CS0212 is a memory safety error that occurs in Unsafe Contexts. The message reads: "You can only take the address of an unfixed expression inside of a fixed statement initializer".
In C#, objects stored on the Managed Heap (like arrays, strings, and class instances) are managed by the Garbage Collector (GC). The GC is allowed to move these objects around in memory to optimize space (compacting). If you were to grab a pointer (a raw memory address) to a managed object, and the GC moved that object while you were using the pointer, your pointer would point to invalid memory, leading to corruption or crashes.
To prevent this, C# requires you to "Pin" (fix) the object in place before taking its address. This error occurs when you try to take the address (&) of a movable managed object without wrapping it in a fixed statement.
Understanding Fixed vs. Unfixed Expressions
- Unfixed Expression: A variable whose memory location is managed by the GC and can change at any moment. Examples:
string,int[],MyClass. - Fixed Statement: A block of code that tells the GC: "Do not move this specific object until I exit this block."
You can only use the address-of operator (&) on a managed object if you are inside that safety declaration.
Stack vs. Heap: Local variables of value types (like int x = 10;) live on the Stack. The GC does not move stack memory. Therefore, you can take the address of &x without a fixed statement. This error only applies to Heap objects.
Scenario 1: Pointers to Strings
Strings in C# are reference types managed by the GC. Internally, they contain a buffer of characters. If you want a pointer to that buffer (char*), you must pin the string.
Example of error
public class TextProcessor
{
public unsafe void ReadChars()
{
string text = "Hello World";
// ⛔️ Error CS0212: You can only take the address of an unfixed expression
// inside of a fixed statement initializer.
// The 'text' string might move in memory at any moment.
char* ptr = &text[0];
// Or simply: char* ptr = text; (Implicit conversion also fails without fixed)
}
}
Solution: The fixed Block
Wrap the pointer assignment in a fixed statement.
public class TextProcessor
{
public unsafe void ReadChars()
{
string text = "Hello World";
// ✅ Correct: The string is pinned for the duration of the block.
fixed (char* ptr = text)
{
// It is safe to use 'ptr' here.
System.Console.WriteLine(*ptr); // Output: H
}
// 'ptr' is no longer valid here, and the string is free to move again.
}
}
Scenario 2: Pointers to Managed Arrays
Arrays (even arrays of primitive types like int[]) are objects on the Heap. You cannot take the address of an element without pinning the array.
Example of error
public class NumberCrusher
{
public unsafe void Process()
{
int[] numbers = { 10, 20, 30 };
// ⛔️ Error CS0212: Attempting to get address of array element without pinning.
int* p = &numbers[0];
}
}
Solution
Use fixed to obtain the pointer to the array (or a specific element).
public class NumberCrusher
{
public unsafe void Process()
{
int[] numbers = { 10, 20, 30 };
// ✅ Correct: Pinning the array.
// 'p' points to the first element by default.
fixed (int* p = numbers)
{
// Access via pointer arithmetic
System.Console.WriteLine(*(p + 1)); // Output: 20
}
}
}
Scenario 3: Pointers to Class Fields
If you have a class (Reference Type) that contains an int field, that int is stored inside the class on the Heap. Therefore, the int moves whenever the class moves. You cannot point to it directly without pinning the entire class instance.
Example of error
public class PointContainer
{
public int X;
public int Y;
}
public class Program
{
public unsafe static void Main()
{
PointContainer pt = new PointContainer();
// ⛔️ Error CS0212: 'pt' is a class (movable).
// Therefore 'pt.X' is also movable.
int* ptrToX = &pt.X;
}
}
Solution
Pin the variable that holds the field.
public class Program
{
public unsafe static void Main()
{
PointContainer pt = new PointContainer();
// ✅ Correct: Pin the field address
fixed (int* ptrToX = &pt.X)
{
*ptrToX = 99;
}
System.Console.WriteLine(pt.X); // Output: 99
}
}
Conclusion
CS0212 is a safeguard against memory corruption caused by the Garbage Collector.
- Identify the Object: Is the variable a
string,array, or inside aclass? If yes, it is on the Managed Heap. - Check the Syntax: Are you trying to use
&varor assignment to a pointer type? - Apply the Fix: Wrap the code in a
fixed (Type* p = var) { ... }block. This effectively pauses the movement of that object so you can safely use pointers.