DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 16.1 Controlling Changes to Pointers Passedto Methods

Problem

You must pass a pointer variable in to a method; however, you do not want to allow the called method to change the address that the pointer passed in is pointing to. For example, a developer wants to assume that after passing in a pointer parameter to a method that that parameter is still pointing to the same address when this 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 this pointer by value, by reference, or as an out parameter. There are several methods of passing arrays to methods. These methods include using or not using the ref or out keywords to define how the parameters are to be handled.

To make sure that a method does not modify the pointer itself, you would 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 the NumPtr pointer is pointing 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 in 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 action allows the SwitchXY method to actually modify where a pointer points to and to return this modified pointer.

Discussion

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

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, we first need to set up a pointer to an integer:

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

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

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

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

CallByValue(ptrx);

If we examine the pointer variable ptrx before the call to CallByValue, we 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 the CallByValue method accepts the pointer ptrx by value. This means that whatever value that ptrx holds, a memory location in this case, it cannot be modified, which is similar to when a reference type is passed.

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

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

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

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

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

CallByRef(ref ptrx);

Now if we examine the value that the pointer ptrx points to, before and after the call is made to CallByRef, we 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 7.

The code in this section of this recipe is meant to be as simple as possible in order to explain the difference between passing a pointer by value, by reference, and as an out parameter. However, there is a serious flaw in the design of this example code (the code in the Solution section does not contain this flaw). Take the following code, for example:

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

    Console.WriteLine(*ptrx);
    SomeOtherMethod("Some Text");
    Console.WriteLine(*ptrx);
}

The called method is written as follows:

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

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 first call to WriteLine displays the correct value (7) for the pointer variable ptrx since the CallUsingOut method's stack frame is still intact. However, the stack frame to the CallUsingOut method is promptly overwritten when the call to SomeOtherMethod is made, thereby causing the second call to WriteLine to display garbage.

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 in the scope of the method that are created on the stack 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 assignging 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.

    [ Team LiB ] Previous Section Next Section