Using Unsafe Code and Pointers in C#

Introduction

One of the biggest features of the .NET platform is the support for type safety. This means that the Common Language Runtime – the engine of .NET - guarantees that there will not be any type errors  (issues which generally arise because of discrepancy between different data types for an application’s constants, variables and methods).

The benefits of type safety include:

  • Illegal operations are avoided.
  • No wild pointers – a pointer of one type is considered as pointer of other type
  • Avoiding buffer overflow

However, there are certain cases where you need access to code to unsafe code – code for which there is no guarantee of type security, e.g. pointers, or calling native APIs (DLLImport).

Any method, type or code block can be defined as unsafe using the unsafe C# keyword.

To compile the code, we need to use the “/unsafe” compiler flag.

Caveat: Tagging your code as unsafe can result in security risks in your code.

The Pointer Basics

In unsafe code, you can declare a type to be a pointer type.

The declaration is shown below:

type* typename;

For example, to declare a pointer to an int, we would declare it as:

int* ptr_MyVal;

If you want to declare multiple pointer types, we declare them as:

int* ptr_MyVal1, ptr_MyVal2;

Pointers can be declared for:

  • all scalar types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal and bool.
  • Enum types
  • Pointer types (pointers to pointers) (type**) - e.g. int**
  • User-defined structs

Let us take a look at a simple C# application to do some pointer arithmetic.

We will create a pointer and use the pointer to add a number to the pointee.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace PointersDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int valueToBeOperated = 10;
            unsafe
            {
                int* ptr_value = &valueToBeOperated;
                Console.WriteLine("valueToBeOperate = " + valueToBeOperated);
                Console.WriteLine("valueToBeOperate via pointer = " +  *ptr_value);
                *ptr_value += 10;
                Console.WriteLine("valueToBeOperate = " + valueToBeOperated);
                Console.WriteLine("valueToBeOperate via pointer = " + *ptr_value);
            }
 
        }
    }
}

In the above listing, we are declaring a pointer to an int and then using the pointer reference to add 10 and then print the output.

To do this, you will have to do two things.

First, the code, which uses pointers will need to be declared in an unsafe context (see the highlighted section).

Next, we need to declare to the compiler  that we want to compile unsafe code. In Visual Studio, we can do it by changing the Project Properties.

Allow Unsafe Code
Allow Unsafe Code

This signals to the compiler to pass the “/unsafe”  flag to the csc.exe compiler.

The output of the above code is:

valueToBeOperate = 10
valueToBeOperate via pointer = 10
valueToBeOperate = 20
valueToBeOperate via pointer = 20

Next, we can take an advanced look to pointers.

In our second case, we will look at pointers to an array.

When we need pointers to objects in the heap, we need to use the fixed keyword to annotate the pointer, to indicate to the compiler that the objects must be fixed to avoid changing the location the pointer is referencing. This results in the object not moving when memory management operations are done.

// Listing 2- Array pointers

int[] myArray = new int[5] { 1, 2, 3, 4, 5 };

                fixed (int* ptr_array = &myArray[0])

                {

                    int* ptr_ptr = ptr_array; // pointer to a pointer

                    Console.WriteLine("*ptr_array = " + *ptr_array);

                    Console.WriteLine("**ptr_ptr = " + *ptr_ptr);

                    *ptr_array += 10;

                    Console.WriteLine("*ptr_array = " + *ptr_array);

                    Console.WriteLine("**ptr_ptr = " + *ptr_ptr);

                    ptr_ptr++;

                    Console.WriteLine("*ptr_array (does not change) = " + *ptr_array);

                    Console.WriteLine("**ptr_ptr (changes to the next array element) = " + *ptr_ptr);

                }

In the above snippet, we declare a pointer to an array, ptr_array. Notice that we need to decorate it with the fixed keyword (highlighted).

When we add 10 to *ptr_array, we are actually dereferencing to the first element and adding 10.

When we increment ptr_ptr (pointer to the pointer to the array), we are telling the pointer to move to the next array element.

The output of the above snippet is:

*ptr_array = 1
**ptr_ptr = 1
*ptr_array = 11
**ptr_ptr = 11
*ptr_array (does not change) = 11
**ptr_ptr (changes to the next array element) = 2

The complete listing of the above code including a working Visual Studio 2013 project is available at PointersDemo

.

Summary

In this article, we learned about using unsafe code and pointers in C#. I hope you have found this information useful.

About the Author

Vipul Patel is a Program Manager currently working at Amazon Corporation. He has formerly worked at Microsoft in the Lync team and in the .NET team (in the Base Class libraries and the Debugging and Profiling team). He can be reached at vipul.patel@hotmail.com



Related Articles

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds