Jaa


unsafe C# code

In all the code I wrote for VSTF I never used unsafe C# code. However, recently I was writing a tool which needed some unsafe code (sizeof in particular).

While writing the code I thought of some quick points to remember while using unsafe code.

Do not use redundant unsafe context

In C#2.0 you no longer need to use unsafe context while applying sizeof to predefined types. This means the following code compiles without any unsafe context.

 class MyClass
{
    static public void Foo()
    {
        Console.WriteLine(sizeof(int));
        Console.WriteLine(sizeof(byte));
    }
}

Limit the scope of the unsafe context

Locate the minimal scope of unsafe code and apply either the unsafe modifier or unsafe statement over that minimal scope only.

The following is bad, because other methods in the class are also included in the unsafe context and unintentinally unsafe code may creep into them.

 unsafe class MyClass
{
    static public void Foo()
    {
        Console.WriteLine(sizeof(MyStruct));
    }
}

The following is better because only required methods are marked as unsafe.

 class MyClass
{
    unsafe static public void Foo()
    {
        Console.WriteLine(sizeof(MyStruct));
    }
}

The following is the best option if you have unsafe code only in limited places, as this gives the unsafe context only to the minimal scope (statement level).

 class MyClass
{
    static public void Foo()
    {
        unsafe
        {
            Console.WriteLine(sizeof(MyStruct));        }
    }
}

Be careful about subtle syntax difference

Most commonly these unsafe features get used by C/C++ programmers moving into C#. The syntax looks deceptively similar to C/C++ but sometimes varies. The following statment is valid C/C++ and valid C# as well.

 int * a, b;

However, in C/C++ it means that a is a pointer and b is not but in C# a and b are both pointers to ints. So in C# * should be written with type as in int* and not with the pointer name.

Becareful about what pointers you return

This is equally true for C/C++. You must ensure that you are not returning a pointer (as return value or ref/out arguments) that points to a local variable. Once the function returns the stack is popped and the local variables are gone. So the returned pointers become dangling pointers if they pointed to some local variables

You can't point to everything

Everything that resides in the manages heap can be moved around (relocated) by the garbage-collector and therefore it is not possible to have pointers to it directly. You can have pointers only to fixed variables. Fixed variables include stuff that is on the stack like local variables and value-type function-parameters. You can also take pointers to variables on the heap after pinning them with a fixed expression

 class MyClass
{
    static int statNum = 50;
    unsafe static public void Bar(int paramNum)
    {
        int localNum;
        int* ap;
        ap = &localNum; //local variable
        ap = &paramNum; //value-type argument
        //pin so that the address don't change
        fixed(int* apnew = &statNum)
        {
            Console.WriteLine(*apnew);
        }
    }
}

Avoid unsafe code and pointers

There was a reson for which pointers were excluded in core C#. Even though pointers make a language very powerfull they introduce complexity and pointer related bugs like dangling pointers, overrun, type-unsafety plague the software. C/C++ programmers have a habit of using unsafe context even when they are not required and this is a habit that may lead to trouble in the .NET world. Use unsafe code only when absolutely necessary....

Comments

  • Anonymous
    February 24, 2006
    Good post, Abhinaba. Unsafe code is pretty useful for interop scenarios, where sometimes there is no convenient alternative.
  • Anonymous
    February 24, 2006
    Thanks and yes and interop defintely makes it into the absolutely necessary condition....
  • Anonymous
    February 24, 2006
    If you are only looking for sizeof, best use Marshal.SizeOf().
  • Anonymous
    February 24, 2006
    I was just waiting for someone to say this :)

    There are differences between sizeof operator and Marshal.Sizeof. Try out the following

    Console.WriteLine(sizeof(char));
    Console.WriteLine(Marshal.SizeOf(typeof(char)));

    and you'd see that the second statement prints out 1 instead of two. The explanation is in http://blogs.msdn.com/shawnfa/archive/2005/02/25/380592.aspx. Now you know why I used sizeof and not Marshal.SizeOf :)
  • Anonymous
    January 16, 2007
    Thanks. I never knew initializing multiple int*s in a single statement caused them all to be pointers...