What's the matter with pointers?
Monday, 25 October 2010
Article Index
What's the matter with pointers?
Inherent dangers
Multiple indirection
Pointers to structs


Pointers in C#

Pointers in C#' are a generalisation of a reference type to include pointer arithmetic.  Pointers are so dangerous that they have to be quarantined within your code. First the entire project has to be marked as unsafe by using the Project Properties to set the Build, Allow Unsafe Code flag. Then any use of pointers has to be enclosed in an usafe{} block to mark it out even more clearly.

Even more restrictive is the fact that you cannot create a pointer to anything, only to a restricted subset of types that have a simpler way of using memory so making pointer use slightly less tricky.

Essentially you can only create a pointer to any simple value type, e.g. int, float, char, to an enum, to another pointer or to a struct that doesn’t contain other managed types.  So you can’t have a pointer to an object, or to a delegate or to a reference.

This is restrictive and basically amounts to not allowing pointers to anything created on the heap or subject to dynamic memory management. However you can have a pointer to a struct that contains simple value types and you can create pointers to arrays of simple value types. You can also have a pointer of type void, i.e. a pointer to an unknown type but to be of any use in, i.e. to use pointer arithmetic, you need to cast a void pointer to a pointer to a given type.

To declare a pointer type C# uses the C++ like syntax:

type* variable;

The * is the dereferencing or indirection operator and is generally used in conjunction with the address of operator &, which as its name suggests, returns the address of a variable.

For example:

int* pMyInt;
int MyInt;
pMyInt = &MyInt;

creates a pointer to an integer, i.e. pMyInt and stores the address of the integer MyInt in the pointer. The first important thing to note is that a pointer does not inherit from an object and so there are no methods associated with it and no boxing and unboxing. For example, you can’t use a ToString() method call to display the value of a pointer.

What you can do, however, is to use a cast to convert a pointer to a more usual data type. So, to display the contents of the pointer you would use something like:


Of course this assumes that the current implementation of int is large enough to contain a pointer, i.e. a machine address. Notice that this is not deferencing the pointer but showing you the pointer's contents.

The indirection operator returns the values stored at the address that a pointer is pointing at. For example:


displays the current contents of MyInt, i.e. the value of whatever pMyInt is pointing at.

The indirection operator and the address of operator really are the inverse of one another. That is:


just displays the content of MyInt. 

A pointer can be null and applying the indirection operator in this case generates an exception. Obviously it makes no sense to use indirection on a void pointer, what would the data type of the result be, but you can always cast a void pointer and then use indirection. Notice that this process can produce complete nonsense. For example, consider:

void* pMyData = &MyInt;

This sets a void pointer to an integer, i.e. a 32-bit integer, then casts it to a double pointer, i.e. double*, and finally uses the indirection operator to return the value so pointed at.

If you try this out you will find that it works but it is mostly nonsense because the original int was only 4 bytes of storage and the double is 8 bytes.

Where did the additional 4 bytes come from?

The answer is that you have succeeded in reading data from a neighbouring memory location, one that you normally would not be able to access. Of course, reading from a memory location that you don’t understand is fairly safe, but who knows what the effect of writing to such a location is going to have.

For example, try:

int MyInt2 = 0;
int MyInt=1234;
void* pMyData = &MyInt;
*(double*)pMyData = 123456.789;

You might be surprised to discover that the value of MyInt2 has changed and is no longer zero, even though it isn’t assigned a new value within the program. The simple explanation is that MyInt2 is allocated storage alongside MyInt and when we assign an 8-byte value to MyInt the extra 4 bytes overwrite MyInt2. This is clearly dangerous, unexpected and usually unwanted behaviour and it is in this sense that the code is “unsafe”.

One very common use of pointers is to get at the internals of a data type. For example, suppose you want to retrieve the four bytes that make up a 32-bit integer:

int MyInt = 123456789;

We can always use a void pointer to get the address of any variable:

void* MyPointer;
MyPointer = &MyInt;

Then we can cast it to a pointer to any of the standard types and use pointer arithmetic, in this case to a byte which is then converted to a char:

byte* pMyByte = (byte*)MyPointer;
char MyChar = (char)*(pMyByte + 3);

The reason we don’t go directly to a char pointer is that a char is two bytes in size and we are converting a 4-byte int to four ASCII rather than Unicode characters.

In most cases there are managed ways of gaining access to the internal structure of the common data types using either the Convert or BitConvertor classes. In this case the BitConvertors GetBytes method can be used to convert the int to a byte array and then any of the bytes can be converted to a char using the Convert class:

Byte[] Bytes=BitConverter.GetBytes(MyInt);
MyChar = Convert.ToChar(Bytes[3]);

As long as there is a GetBytes method that will convert the data type into a byte array you don’t need to use pointers.








Last Updated ( Monday, 25 October 2010 )

RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.