www.gibmonks.com

Table of Contents

Previous Page
Next Page

Recipe 19.1. Controlling Changes to Pointers Passed to Methods

Problem

You must pass a pointer variable to a method; however, you do not want to allow the called method to change the address that the pointer points to. For example, a developer wants to assume that, after passing a pointer parameter to a method, the parameter is still pointing to the same address when the method returns. If the called method were to change what the pointer pointed to, bugs could be introduced into the code.

In other cases, the converse may be true: the developer wants to allow the address to be changed in the method she passes the pointer to. Consider a developer who might create a method that accepts two pointers and switches those pointers by switching the memory locations to which each pointer points to, rather than swapping the values each pointer points to.

Solution

You must decide whether to pass a pointer by value, by reference, or as an out parameter. There are several ways to pass pointers to methods. These include using or not using the ref or out keyword to define how the parameters are to be handled.

To make sure that a method does not modify the pointer itself, pass the pointer by value, as shown here:

	unsafe
	{
	    int num = 1;
	    int* numPtr = #
	    ModifyValue(numPtr);
	    // Continue using numPtr…
	}

The method ModifyValue can still change the value in the memory location to which numPtr points to, but it cannot force numPtr to point to a different memory location after the method ModifyValue returns.

To allow the method to modify the pointer, pass it by reference:

	public unsafe void TestSwitchXY( )
	{
	    int x = 100;
	    int y = 20;
	    int* ptrx = &x;
	    int* ptry = &y;

	    Console.WriteLine(*ptrx + "\t" + (int)ptrx);
	    Console.WriteLine(*ptry + "\t" + (int)ptry);

	    SwitchXY(ref ptrx, ref ptry);
	    Console.WriteLine(*ptrx + "\t" + (int)ptrx);
	    Console.WriteLine(*ptry + "\t" + (int)ptry);
	}

	public unsafe void SwitchXY(ref int* x, ref int* y)
	{
	    int* temp = x;
	    x = y;
	    y = temp;
	}

The SwitchXY method switches the values of the x and y pointers so that they point to the memory location originally pointed to by the other parameter. In this case, you must pass the pointers in to the SwitchXY method by reference (ref). This allows the SwitchXY method to actually change the pointers and to return these modified pointers.

Discussion

In safe code, passing a value type to a method by value means that the value, not the reference to that value, is passed in. Therefore, the called method cannot modify the value that the calling method's reference points to; it can modify only the copy that it receives.

It works the same way with unsafe code. When an unsafe pointer is passed in to a method by value, the value of the pointer (which is a memory location) cannot be modified; however, the value that this pointer points to can be modified.

To examine the difference between passing a pointer by reference and by value, you first need to set up a pointer to an integer:

	int x = 5;
	int* ptrx = &x;

Next, write the method that attempts to modify the pointer parameter:

	private unsafe void CallByValue(int* x)
	{
	    int newNum = 7;
	    x = &newNum;
	}

Finally, call the method and pass in ptrx to this method:

	CallByValue(ptrx);

If you examine the pointer variable ptrx before the call to CallByValue, you'll see that it points to the value 5. The called method CallByValue changes the passed-in parameter to point to a different memory location. However, when the CallByValue returns, the ptrx pointer still points to the original memory location that contains the value 5. The reason for this is that CallByValue accepts the pointer ptrx by value. This means that whatever value ptrx holds, a memory location in this case, it cannot be modified.

At other times, you need to allow a called method to modify the memory location that a pointer points to. Passing a pointer by reference does this. This means that the called method may, in fact, modify the memory location to which a pointer parameter points. To see this, again set up a pointer:

	int x = 5;
	int* ptrx = &x;

Next, write the method that attempts to modify the parameter:

	private unsafe void CallByRef(ref int* x)
	{
	    int newNum = 7;
	    x = &newNum;
	}

Finally, call the method and pass the pointer by reference:

	CallByRef(ref ptrx);

Now if you examine the value that the pointer ptrx points to, before and after the call is made to CallByRef, you'll see that it has indeed changed from 5 to 7. Not only this, but the ptrx pointer is actually pointing to a different memory location. Essentially, the ref keyword allows the method CallByRef to modify the value contained in the ptrx variable.

Let's consider the use of the out or ref keywords with pointers. A method that accepts a pointer as an out or ref parameter is called like this:

	public unsafe void TestOut( )
	{
	    int* ptrx;
	    CallUsingOut(out ptrx);

	    Console.WriteLine(*ptrx + "\t" + (int)ptrx);
	}

The CallUsingOut method is written as follows:

	public unsafe void CallUsingOut(out int* ptrx)
	{
	    int x = 7;
	    ptrx = &x;
	}

The ptrx variable is initially a null pointer. After the call is made to the CallUsingOut method, the ptrx variable points to the value 0. There is a serious flaw in the design of this example code (the code in the Solution section does not contain this flaw).

The problem is that the temp variable, pointed to by the out parameter ptrx in the CallUsingOut method, is in the stack frame of the CallUsingOut method. The stack frame to the CallUsingOut method is promptly overwritten when this method returns, thereby causing the value in ptrx to be undefined.

This mistake is easy to make, especially as the code gets more and more complex. This error can also occur when returning a pointer from a method as a return value. To solve this, you need to not assign local variables that are created on the stack in the scope of the method to the pointer, since the value being pointed to can "go away" once the scope is exited, creating a dangling pointer.

Be very careful that you do not create dangling pointers (a pointer that doesn't point at anything valid, such as by assigning a pointer to memory that is collected before leaving the function) when passing pointer parameters as ref or out. This warning also applies to pointers used as return values.


See Also

See the "Method Parameters," "out Parameter," and "ref Parameter" topics in the MSDN documentation.


Previous Page
Next Page