Reader Level:
ARTICLE

Using Pointers in C#

Posted by Phil Curnow Articles | C# Language October 02, 2007
This article explains the concept of pointers and how you implement them in C# should you need to.
  • 0
  • 0
  • 87555

Back in the late 1980's and up to around 1997, most of my development work was done using the C programming language. Anyone who has done a fair amount of work in C or C++ would not have been able to avoid the use of pointers. Recently I had to re-visit some old code I had written in C and one of my first comments was 'wow pointers, I remember them!'

I have known since I started using C# (and that's been a few years) that you could use pointers, but to be honest, I have never had to use them and have never gone looking for a reason to use them. The main reasons I have written this article is to introduce the concept of pointers to any developers who have not seen them before, and as a little nostalgic trip back to the days when they were part of my daily coding diet!

So, what is a Pointer?

A pointer is nothing more than a variable that holds the address in memory of another variable. In C#, pointers can only be used on value types and arrays. As a structure is a value type, pointers can be used with them, but there is one caveat with this, the structure must not contain any reference types if you plan to use pointers. Any of the following may be a pointer:

  • Sbyte
  • byte
  • short
  • ushort
  • int
  • uint
  • long
  • ulong
  • char
  • float
  • double
  • decimal
  • bool

unsafe keyword:

Typically when we write code in C# by default it is what's known as safe code. Unsafe code allows you to manipulate memory directly, in the normal world of C# and the .NET Framework, this is seen as potentially dangerous, and as such you have to mark your code as unsafe. Using unsafe code, you are loosing garbage collection and whilst directly accessing memory, you have the possibility of accessing memory that you didn't want to and causing untold problems with your application. Unsafe code can only be used within a fully trusted assembly.

Now it is time to write some program code to demonstrate the use of pointers. Consider the following code.

static void Main(string[] args)
{
    int age = 32;
    Console.WriteLine("age = {0}", age);
}

A very simple piece of code that will print age = 32 on the console. Now let's introduce a pointer into the mix. When we declare a pointer, we have to declare it in a certain way, this being type* variable; the asterisk (dereferencer symbol) informs the compiler that we are declaring a pointer variable, the type says that we intend to use a pointer to store the address of type type. So using our simple piece of code above, let's creates an integer pointer to point to age.

static void Main(string[] args)
{
    unsafe
    {
        int age = 32;
        int* age_ptr;

        Console.WriteLine("age = {0}", age);

    }
}

Notice the use of the unsafe keyword, if we had not enclosed our code within unsafe, we would have received the following compilation error.

Pointers and fixed sized buffers may only be used in an unsafe context.

We could have declared the whole method as unsafe and this would have worked fine, for example:

unsafe static void Main(string[] args)

So, what can we do with our pointer? As stated at the beginning of the article, pointers hold addresses in memory to other variables, so we need to make age_ptr point to the memory location of age. To do this, we use the following line of code:

age_ptr = &age;

The ampersand '&' is the referencer and means the 'location of'. So if we now do the following in our code:

static void Main(string[] args)

{

    unsafe

    {

        int age = 32;

        int* age_ptr;

        age_ptr = &age

        Console.WriteLine("age = {0}", age);

        Console.WriteLine("age_ptr = {0}", *age_ptr);

    }

}

We should now see the following output on the console:

age = 32
age_ptr = 32

Basically, with the display line for age_ptr, we are printing the value held in the memory location that age_ptr is pointing to (in this instance, the age variable).

Now, what happens if we add the following to the example?

 

*age_ptr += 3;

Console.WriteLine("age now = {0}", age);

 

What do you think the output will be (note that we are printing the value of age)? If you have said 35 you are correct! The value of age has been changed to 35. Let's dissect the line *age_ptr += 3 and see what's happening.

age_ptr has been declared as an integer, the term *age_ptr can be read as 'the integer value at the memory location age_ptr is pointing to'. We are then simply adding 3 to the integer value, giving us a result of 35. As age_ptr is pointing to the memory location where age stores its value, essentially we have modified the variable age.

Pointers to Structures:

As previously mentioned, you can define a pointer to a structure, but you have to make sure that there are no reference types in the structure, for example, consider the example code below:

class TestClass
{
    . . .
}

struct TestStruct
{
    public int age;
    public TestClass test;
}

You would not be able to then do the following:

TStruct test_struct;
TStruct* struct_ptr;
struct_ptr = &test_struct;

When compiling this code, you would receive the error message:

Cannot take the address of, get the size of, or declare a pointer to a managed type.

So let's create a valid structure and see how we use a pointer to the structure. Our structure will be a simple one that contains two integers X and Y to simulate co-ordinates on a screen.

struct Location
{
    public int X;
    public int Y;
}

Now we can create the following code:

unsafe static void Main(string[] args)
{
    Location loc;
    loc.X = 100;
    loc.Y = 100;

    Location* loc_ptr;
    loc_ptr = &loc;
}

There should be no surprises in the code above. loc_ptr is of the type Location, which is our structure. So how do we use our pointer? To access the X and Y members of our structure through the use of our pointer, we can use the following syntax

(*loc_ptr).X;  or  loc_ptr->X;

I know which one I would rather use (the second option), but both perform exactly the same function. So if we wanted to display and modify X we can do this as follows:

unsafe static void Main(string[] args)

{

    Location loc;

    loc.X = 100;

    loc.Y = 100;

 

    Location* loc_ptr;

    loc_ptr = &loc;

    Console.WriteLine("X = {0}", loc_ptr->X);

 

    loc_ptr->X = 200;

    Console.WriteLine("X = {0}", loc_ptr->X);

}

Pointers and Classes:

A class is a reference type, so therefore you cannot define a pointer to a class, but you can define a pointer to a class member that is a value type. Before we see an example of how to do this, there is something that you need to understand before creating pointers to value type members of a class.

When using safe code, the garbage collector will possibly move an object in memory during its lifetime, this is done to manage free resources and stop fragmentation. If you have a pointer to a value type in a class, this could cause some unexpected results, and some headaches! Needless to say, there is a way around this problem. We can instruct the garbage collector not to move specific objects. We do this by using the fixed keyword. Let's take a look at an example.

The design of the following class is not ideal and any OO design guru's out there will probably be shaking their heads at it, but it has been done as a very simple example.

For Example:

 

class PTest

{

    public PTest()

    {

    }

    public int age;

    public void DisplayAge()

    {

        Console.WriteLine("age = {0}", age);

    }

}

And some example code to create an instance of the class and then manipulate the public variable age using a pointer.

unsafe static void Main(string[] args)
{
    PTest p = new PTest();

    p.age = 32;
    p.DisplayAge();

    fixed (int* age = &p.age)
    {
        *age += 3;
    }

    p.DisplayAge();
}

Notice the use of the keyword fixed. Basically what this is ensuring is that during the execution of the code within the fixed statement, the object p will not be moved, thus solving the problem of our pointer pointing to an invalid memory location, this operation is called 'pinning'.

Compilation of unsafe code:

In order to compile program code that contains unsafe code, you need to inform the compiler. When using the command line compiler, you need to issue the /unsafe switch. When using Visual Studio, you need to tick the box in the project properties titled Allow Unsafe code (this is found within the Build section).

Conclusion:

Using pointers in C# is almost never required and they are generally only used in conjunction with P/Invoke. If you have a requirement to use pointers, you need to be very careful as you are manipulating memory directly.

My advice would be, don't go looking for a reason to use them, if you can perform your task using safe code, do so.

COMMENT USING

Trending up