Skip to content

Unsafe Code and Pointers

Unsafe code allows direct memory-oriented operations that normal C# does not allow. This includes pointer syntax, direct addresses, and certain low-level memory manipulations.

It is called unsafe because the compiler and runtime give up some of the normal guarantees that make ordinary C# safer and easier to reason about.

Why unsafe code exists

Most C# code should stay in safe managed code. Unsafe code exists for specialized scenarios such as:

  • interop with native APIs
  • very low-level performance work
  • special memory manipulation tasks
  • working with buffers in ways that ordinary safe syntax cannot express directly

It is not a general-purpose style for everyday business logic.

A basic pointer example

csharp
unsafe
{
    int value = 10;
    int* pointer = &value;
    Console.WriteLine(*pointer);
}

This code does three important things:

  • &value gets the address of value
  • int* declares a pointer to an int
  • *pointer dereferences the pointer to read the pointed-at value

A mental model

mermaid
flowchart LR
    A["Variable value = 10"] --> B["Memory location"]
    C["pointer"] --> B
    C --> D["Dereference with * to read or write"]

The pointer does not store the integer value itself. It stores a memory address that can be used to access the value.

What makes it unsafe

In safe C# code, the runtime helps protect you from many memory mistakes. In unsafe code, you can create problems such as:

  • reading invalid memory
  • writing to the wrong location
  • corrupting data
  • causing crashes or undefined behavior

That is why unsafe code requires extra care and explicit project support.

unsafe context

Pointer operations must appear in an unsafe context.

csharp
unsafe class BufferReader
{
}

or

csharp
unsafe
{
}

This makes the low-level intent explicit.

A practical caution

Unsafe code is often discussed together with performance, but unsafe does not automatically mean faster in a meaningful real-world way. Modern C# and .NET already provide many efficient safe APIs.

For example, Span<T>, Memory<T>, and optimized library code often provide the benefits people want without dropping into raw pointers.

When pointers are more reasonable

Pointers are more reasonable when:

  • calling native code requires them
  • you are working in a tightly controlled low-level boundary
  • profiling has shown a real need
  • safe alternatives are not sufficient for the scenario

When to avoid them

Avoid unsafe code when:

  • ordinary collections or spans are enough
  • the code is application logic rather than low-level systems code
  • the team cannot confidently review and maintain pointer-heavy code
  • the performance benefit is only guessed, not measured

Common beginner mistakes

  • Assuming unsafe code is the mark of advanced or better C#.
  • Using pointers before understanding safer alternatives.
  • Treating unsafe code as a shortcut rather than a specialized tool.
  • Forgetting that memory mistakes here can become serious runtime problems.

Summary

  • unsafe code allows pointer-based memory operations in C#
  • it is intended for specialized low-level scenarios
  • pointer syntax works with addresses and dereferencing
  • it reduces many of the normal safety guarantees of managed code
  • safe alternatives should usually be preferred unless there is a real technical reason not to

Practice

Explain in plain language what &value, int*, and *pointer mean in the basic example.

As a second exercise, describe one realistic scenario where unsafe code may be justified and one where ordinary safe C# would clearly be the better choice.

Built with VitePress and published on GitHub Pages.