Unsafe Context in C#: Working With Pointers

Did you know that you can, just like in C, use pointers in C#?

To be able to use pointers in C#, we need to introduce the unsafe keyword and understand how unsafe contexts work. This article explains what unsafe code is, how pointers can be used in C#, and when using unsafe code might make sense.

TL;DR

Unsafe code lets us use pointers and pointer operations inside C# code. We can only use pointers inside an unsafe code block. Classes, structs and functions can be declared as unsafe, letting us write unsafe code within them.

Fixed arrays can only be declared inside unsafe structs, not classes. The keyword fixed pins a variable address on the heap and doesn’t let the garbage collector move it. To iterate through an array, we need to use the fixed keyword.

For 99% of situations, you won’t need to use the unsafe feature, but it can come in handy if your situation is in the other 1%.

What Is Unsafe Context in C#?

To be able to use and compile code that uses the unsafe keyword, we have to enable the AllowUnsafeBlocks compiler option.

We can enable it by editing the .csproj file and adding the following:

After enabling it, we can start messing around with pointers.

What are Pointers in C#?

What are pointers? Pointers are special variables that store the memory address of another variable. We can obtain the value of a memory location by dereferencing the pointer (we won’t be going in-depth into pointers, memory and such in this post).

So, how do we use pointers in C#? Well, we can only use pointers and pointer operations inside an unsafe block.

Example: Declaring and Using a Pointer

Pointers allow direct memory access, but they must always be used carefully to avoid unintended behavior.


Unsafe Functions

We can also declare unsafe functions, which marks all the code inside the function as unsafe, and lets us write unsafe code inside the function.

Example: Unsafe Function

To use this function, we have to declare a block of code as unsafe. Like in the example bellow:

Using Unsafe With Structs and Classes

Structs and classes can also be marked as unsafe. One key difference is that in an unsafe struct, we can declare a fixed array inside a struct, while we cannot do that inside an unsafe class.

Unsafe Structs and Fixed Arrays

Unsafe Classes

Declaring fixed arrays inside unsafe classes is not allowed and will result in a compilation error.

Marking a class as unsafe (just as with marking a function as unsafe) lets us write unsafe code inside the class without using the unsafe keyword.

The fixed Keyword Explained

You might have noticed the fixed keyword and asked yourselves what it means, well the keyword “fixed” prevents the variable from moving by pinning it on the heap.

This keyword is primarily used with:

  • Array elements
  • Strings (since strings can be treated as character arrays)

What causes the variable to move? The garbage collector can move the variable to another address. Since the address of a fixed variable cannot be changed, we can be pretty sure that our pointer points to the correct memory address and to the correct value located at that address.

The next example demonstrates changing a character in a string by using the unsafe context in C#.

The above code gives the following output:

Benchmark: Comparing Performance

And last, but certainly not least, here is a benchmark that shows the difference in speed and memory efficiency of changing a letter in a string. There are 4 methods used in this benchmark:

  • .Substrings
  • StringBuilder
  • Spans
  • Pointers (unsafe)

The string used for this benchmark is the following

string word = “This benchmark should show a difference in speed and memory allocation.”;

In this string, we are changing the full stop at the end of the string into an exclamation mark.  

Let’s take a look at the benchmark results.

We can see that using pointers to change a letter in a string is a lot faster and no memory is allocated. Why is that? All these methods, except the unsafe method (which uses pointers), create a copy of the string we want to change and that new string holds our change. This means that, effectively we have two different strings, one without the change and one with the change.

The method that uses the pointer, on the other hand, accesses the memory location of the string, finds the position of the character that needs to be changed, and changes it. This does not allocate any memory since we are not creating a new instance of a string, but just changing a character at a specific memory location.

To show this, we can check for reference equality

The result of this code is as follows:

Should You Use Unsafe Code?

Probably not. If you are writing performance critical code, then you might consider using unsafe code (or you could also consider using a lower level programming language like C).

 But, beware that if you are not comfortable with what you are doing, you might cause more issues, even having the code run slower than just not using unsafe code.

Conclusion

While using unsafe code in C# might give you some performance benefits, it doesn’t mean that you should use unsafe code. In 99% of situations, you won’t need to use unsafe code, but for the 1% of situations, it is good to know that unsafe code exists and that it could help you.

If you decide that your situation is in those 1%, then be careful with using this feature as it might cause more issues if you are not comfortable with pointers and lower level code.

Additional Resources

For more information about unsafe context in C#, see the official documentation:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/unsafe

Related Posts

Leave a Reply

Contact Us