Chapter 16. Unsafe Code

Visual C# .NET (C#) allows you to step outside of the safe environment of managed code and write code that is considered “unsafe” by the Common Language Runtime (CLR). Running code that is considered unsafe by the CLR presents a certain set of restrictions in exchange for opening up possibilities like accessing memory-mapped data or implementing time-critical algorithms that use pointers directly. These restrictions are mainly based in the Code Access Security (CAS) system of the CLR and are in place to draw a distinct line between code the CLR knows to be playing by the rules (or “safe”), and code that needs to do a bit outside of the traditional sandbox of the CLR (and is thus “unsafe” code). In order to run code that is marked as unsafe by the CLR, you need the CAS SkipVerification privilege granted to the assembly that the unsafe code is implemented in. This tells the CLR to not bother verifying the code and to allow it to run, whereas normally unverified code would not run. This is a highly privileged operation and is not to be done lightly, as you increase the permissions your application will require in order to operate correctly on a user’s system. If you use unsafe types in a method signature, you also make the code non-CLS-compliant. This means that interoperability with other .NET based languages, like VB.NET or Managed C++, for this assembly is compromised.

Even though unsafe code allows you to easily write potentially unstable code, it does have several safeguards. Only value types or pointers to value types inside of reference types can be used with unsafe code; reference types cannot. This allows pointer types to be created solely on the stack, so you do not have to use the new and delete operations to allocate and release memory to which the variable points. You only have to wait for the method that declared the pointer type to return, forcing the pointer to go out of scope and clearing any stack space devoted to this method. You can get into a bit of trouble if you are doing exotic things with unsafe code (such as pointing to a value type inside of a reference type) since this behavior allows access to heap-based memory and opens up the possibility for pointer pitfalls such as those seen in C++.

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 + "	" + (int)ptrx);
    Console.WriteLine(*ptry + "	" + (int)ptry);

    SwitchXY(ref ptrx, ref ptry);

    Console.WriteLine(*ptrx + "	" + (int)ptrx);
    Console.WriteLine(*ptry + "	" + (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 + "	" + (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.

Warning

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.82.217