Skip to main content

Command Palette

Search for a command to run...

Understanding the fixed Keyword in C#

Pinning Memory Like a Pro

Published
3 min read
Understanding the fixed Keyword in C#
M

Morteza Jangjoo, Senior .NET Backend Developer with 15+ years of experience in C#, ASP.NET Core, SQL Server, and Microservices. Skilled in building scalable, high-performance systems.

When working with C#, most of the time we don’t need to think about memory management. The Garbage Collector (GC) automatically allocates, moves, and frees memory for us.
But sometimes, especially when we interact with unmanaged code or use unsafe blocks, we need to make sure that a piece of memory stays exactly where it is.

That’s where the mysterious fixed keyword comes in. 🚀


What Does fixed Do?

By default, the GC can move objects around in memory (a process called compacting) to optimize memory usage.
If you take a raw pointer to a managed object, there’s no guarantee that the pointer will still be valid after the next GC cycle.

fixed pins an object in memory, telling the GC:

“Don’t move this object until I’m done with it.”

This is especially useful when you need to pass a pointer to unmanaged code or work directly with raw memory.


Example 1: Pinning an Array

using System;

class Program
{
    unsafe static void Main()
    {
        int[] numbers = { 10, 20, 30, 40, 50 };

        fixed (int* ptr = numbers)
        {
            for (int i = 0; i < numbers.Length; i++)
            {
                Console.WriteLine(ptr[i]); // Direct pointer access
            }
        }
    }
}

Without fixed, the GC could move the numbers array around, invalidating the pointer.
With fixed, we guarantee the pointer stays valid until the block ends.


Example 2: Working with Native APIs

Let’s say you need to call a Windows API or a C library that expects a pointer.
You can pin your data and pass its address safely:

using System;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern bool SetWindowText(IntPtr hWnd, string text);

    unsafe static void Main()
    {
        string message = "Hello from C#!";

        fixed (char* p = message)
        {
            SetWindowText((IntPtr)123456, new string(p));
        }
    }
}

Here, fixed ensures that the message string is not relocated by the GC while we pass it to a native API.


Example 3: Image Processing (High Performance)

In graphics programming, we often need direct access to pixel buffers:

using System;
using System.Drawing;
using System.Drawing.Imaging;

class Program
{
    static void Main()
    {
        Bitmap bmp = new Bitmap(100, 100);
        BitmapData data = bmp.LockBits(
            new Rectangle(0, 0, bmp.Width, bmp.Height),
            ImageLockMode.ReadWrite,
            PixelFormat.Format32bppArgb);

        unsafe
        {
            byte* ptr = (byte*)data.Scan0;

            // Pin a buffer (example usage)
            fixed (byte* start = new byte[data.Stride * data.Height])
            {
                for (int y = 0; y < data.Height; y++)
                {
                    for (int x = 0; x < data.Width; x++)
                    {
                        int index = y * data.Stride + x * 4;
                        ptr[index] = 0;     // Blue
                        ptr[index + 1] = 0; // Green
                        ptr[index + 2] = 255; // Red
                        ptr[index + 3] = 255; // Alpha
                    }
                }
            }
        }

        bmp.UnlockBits(data);
        bmp.Save("output.png");
    }
}

Here, fixed guarantees that our memory buffer is pinned while we manipulate pixel data directly.


Important Considerations

  • Performance impact: Pinning prevents the GC from moving objects, which can cause memory fragmentation if overused.

  • Use only when necessary: You don’t need fixed in everyday C# development. It’s mostly for interop, unsafe code, and high-performance scenarios.

  • Prefer safe APIs when available: For many cases, classes like Span<T>, Memory<T>, and Marshal provide safer abstractions.


Summary

  • fixed is a special keyword in C# used to pin memory.

  • It ensures a managed object won’t be relocated by the GC.

  • It’s essential for scenarios involving:

    • P/Invoke with native APIs

    • Unsafe pointer manipulation

    • High-performance graphics, networking, and data processing

Think of it as telling the GC:

“Hands off this object — I need to work with it directly.”


Have you ever used fixed in your projects? Was it for interop, performance tuning, or just curiosity? Share your experience in the comments!


Get sample code from github

I’m Morteza Jangjoo and “Explaining things I wish someone had explained to me”