Introduction
In unsafe code or in other words unmanaged code it is possible to declare and use pointers. But the
question is why do we write unmanaged code? If we want to write code that interfaces with the operating
system, or want to access memory mapped device or want to implement a time critical algorithm then the
use of pointer can give lots of advantages.
But there are some disadvantages of using pointers too. If pointers were chosen to be 32 bit
quantities at compile time, the code would be restricted to 4gig of address space, even if it were run
on a 64 bit machine. If pointers were chosen at compile time to be 64 bits, the code could not be run
on a 32 bit machine.
In C# we can write unsafe code with the modifier unsafe
. All the unsafe code must be
clearly marked with the unsafe
modifier. Writing unsafe code is much like writing C code
in a C# program.
Now let's see the first program
Program 1
using System;
class MyClass {
public static void Main() {
int iData = 10;
int* pData = &iData;
Console.WriteLine("Data is " + iData);
Console.WriteLine("Address is " + (int)pData );
}
}
Here I use a pointer in this program. Now compile this program. The compiler gives the error
Microsoft (R) Visual C# Compiler Version 7.00.9030 [CLR version 1.00.2204.21]
Copyright (C) Microsoft Corp 2000. All rights reserved.
um1.cs(6,8): error CS0214: Pointers may only be used in an unsafe context
um1.cs(8,27): error CS0214: Pointers may only be used in an unsafe context
Now let's change the program a little bit and add unsafe modifier with the function.
Program 2
using System;
class MyClass {
public unsafe static void Main() {
int iData = 10;
int* pData = &iData;
Console.WriteLine("Data is " + iData);
Console.WriteLine("Address is " + (int)pData );
}
}
In this program the Main() function is defined as unsafe so we can use pointer in this function.
The output of the program is
Data is 10
Address is 1244316
It is not necessary that we define the unsafe
modifier with the function. We can
define a block of unsafe code. Let's change a program little bit more.
Program 3
using System;
class MyClass {
public static void Main() {
unsafe {
int iData = 10;
int* pData = &iData;
Console.WriteLine("Data is " + iData);
Console.WriteLine("Address is " + (int)pData );
}
}
}
In this program a block is defined with unsafe
modifier. So we can use pointers in
that code. The output of this program is the same as previous one.
Now let's change the program a little bit to get a value from the pointer.
Program 4
using System;
class MyClass {
public static void Main() {
unsafe {
int iData = 10;
int* pData = &iData;
Console.WriteLine("Data is " + iData);
Console.WriteLine("Data is " + pData->ToString() );
Console.WriteLine("Address is " + (int)pData );
}
}
}
We can use the ToString()
member function to get data from pointer. Lets change the
program a little bit to see it's behavior.
Program 5
using System;
class MyClass {
public static void Main() {
testFun();
}
public static unsafe void testFun() {
int iData = 10;
int* pData = &iData;
Console.WriteLine("Data is " + iData);
Console.WriteLine("Address is " + (int)pData );
}
}
In this program a function with unsafe
modifier is called from a normal function.
This program shows that a managed code can call unmanaged functions. The output of the program is the
same as previous program.
Now change the program little bit and make an unsafe function in another class.
Program 6
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.testFun();
}
}
class TestClass {
public unsafe void testFun() {
int iData = 10;
int* pData = &iData;
Console.WriteLine("Data is " + iData);
Console.WriteLine("Address is " + (int)pData );
}
}
The output of the program is same as previous one.
Now try to pass pointer as a parameter. Let’s see this program.
Program 7
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.testFun();
}
}
class TestClass {
public unsafe void testFun() {
int x = 10;
int y = 20;
Console.WriteLine("Before swap x = " + x + " y= " + y);
swap(&x, &y);
Console.WriteLine("After swap x = " + x + " y= " + y);
}
public unsafe void swap(int* p_x, int *p_y) {
int temp = *p_x;
*p_x = *p_y;
*p_y = temp;
}
}
In this program the unsafe function testFun()
calls the classic swap()
function to interchange the value of two variables passing by reference. Now change the program a little
bit.
Program 8
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
unsafe {
int x = 10;
int y = 20;
Console.WriteLine("Before swap x = " + x + " y= " + y);
Obj.swap(&x, &y);
Console.WriteLine("After swap x = " + x + " y= " + y);
}
}
}
class TestClass {
public unsafe void swap(int* p_x, int* p_y) {
int temp = *p_x;
*p_x = *p_y;
*p_y = temp;
}
}
This program does the same job as previous one. But in this program we write only one unsafe
function and call this function from the unsafe block in Main
.
Now let's see another program which show the usage of array in C#
Program 9
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.fun();
}
}
class TestClass {
public unsafe void fun() {
int [] iArray = new int[10];
for (int iIndex = 0; iIndex < 10; iIndex++) {
iArray[iIndex] = iIndex * iIndex;
}
for (int iIndex = 0; iIndex < 10; iIndex++) {
Console.WriteLine(iArray[iIndex]);
}
}
}
This program display the square of numbers from zero to 9.
Let's change the program a little bit and pass the array as a parameter to a function.
Program 10
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.fun();
}
}
class TestClass {
public unsafe void fun() {
int [] iArray = new int[10];
for (int iIndex = 0; iIndex < 10; iIndex++) {
iArray[iIndex] = iIndex * iIndex;
}
testFun(iArray);
}
public unsafe void testFun(int [] p_iArray) {
for (int iIndex = 0; iIndex < 10; iIndex++) {
Console.WriteLine(p_iArray[iIndex]);
}
}
}
The output of the program is same as previous one.
Now let's change the program a little bit and try to get the value of the array from a pointer rather
than an index.
Program 11
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.fun();
}
}
class TestClass {
public unsafe void fun() {
int [] iArray = new int[10];
for (int iIndex = 0; iIndex < 10; iIndex++) {
iArray[iIndex] = iIndex * iIndex;
}
for (int iIndex = 0; iIndex < 10; iIndex++) {
Console.WriteLine(*(iArray + iIndex) );
}
}
}
Here in this program we try to access the value of the array from *(iArray + iIndex)
rather than iArray[iIndex]
. But the program gives the following error.
Microsoft (R) Visual C# Compiler Version 7.00.9030 [CLR version 1.00.2204.21]
Copyright (C) Microsoft Corp 2000. All rights reserved.
um11.cs(21,24): error CS0019: Operator '+' cannot be applied to operands of type 'int[]' and 'int'
In C# int*
and in[]
are not treated the same. To understand it more
let's see one more program.
Program 12
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.fun();
}
}
class TestClass {
public unsafe void fun() {
int [] iArray = new int[10];
iArray++;
int* iPointer = (int*)0;
iPointer++;
}
}
There are two different types of variable in this program. First, the variable iArray
is declared an array and the second variable iPointer
is a pointer variable. Now I am
going to increment both. We can increment the pointer variable because it is not fixed in memory but we
can't increment the iArray
, because the starting address of the array is stored in
iArray
and if we are allowed to increment this then we will lose starting address of array.
The output of the program is an error.
Microsoft (R) Visual C# Compiler Version 7.00.9030 [CLR version 1.00.2204.21]
Copyright (C) Microsoft Corp 2000. All rights reserved.
um12.cs(13,3): error CS0187: No such operator '++' defined for type 'int[]'
To access the element of the array via a pointer we have to fix the pointer so it can't be
incremented. C# uses the fixed
reserve word to do this.
Program 13
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.fun();
}
}
class TestClass {
public unsafe void fun() {
int [] iArray = new int[10];
for (int iIndex = 0; iIndex < 10; iIndex++) {
iArray[iIndex] = iIndex * iIndex;
}
fixed(int* pInt = iArray)
for (int iIndex = 0; iIndex < 10; iIndex++) {
Console.WriteLine(*(pInt + iIndex) );
}
}
}
We can use the same technique to pass the array to a function which receives the pointer as
a parameter.
Program 14
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.fun();
}
}
class TestClass {
public unsafe void fun() {
int [] iArray = new int[10];
for (int iIndex = 0; iIndex < 10; iIndex++) {
iArray[iIndex] = iIndex * iIndex;
}
fixed(int* pInt = iArray)
testFun(pInt);
}
public unsafe void testFun(int* p_pInt) {
for (int iIndex = 0; iIndex < 10; iIndex++) {
Console.WriteLine(*(p_pInt + iIndex) );
}
}
}
The output of the program is the same as the previous one. If we try to access beyond the array
limit then it will print garbage.
Program 15
using System;
class MyClass {
public static void Main() {
TestClass Obj = new TestClass();
Obj.fun();
}
}
class TestClass {
public unsafe void fun() {
int [] iArray = new int[10];
for (int iIndex = 0; iIndex < 10; iIndex++) {
iArray[iIndex] = iIndex * iIndex;
}
fixed(int* pInt = iArray)
testFun(pInt);
}
public unsafe void testFun(int* p_pInt) {
for (int iIndex = 0; iIndex < 20; iIndex++) {
Console.WriteLine(*(p_pInt + iIndex) );
}
}
}
Here we try to read 20 elements from array but there are only 10 elements in the array so it will
print garbage after printing the elements of array.
Program 16
using System;
struct Point {
public int iX;
public int iY;
}
class MyClass {
public unsafe static void Main() {
Point refPoint = new Point();
refPoint.iX = 10;
refPoint.iY = 20;
Point* pPoint = &refPoint;
Console.WriteLine("X = " + pPoint->iX);
Console.WriteLine("Y = " + pPoint->iY);
Console.WriteLine("X = " + (*pPoint).iX);
Console.WriteLine("Y = " + (*pPoint).iY);
}
}
Here pPoint
is the pointer of Point class instance. We can access the element of it by
using the ->
Operator.
Change in Beta 2
When you want to compile program using command line switch you type the program name after the
compiler name; for example if your program name is prog1.cs then you will compile this:
scs prog1.cs
This works fine for unsafe code while you are programming in beta 1. In beta 2 Microsft added one
more switch to command line compiler of C# for writing unsafe code. Now if you want to write unsafe
code then you have to specify the /unsafe
command line switch with command line compiler otherwise
the compiler gives an error. In beta 2 if you want to write unsafe code in your program then you
compile your programas follows:
csc /unsafe prog1.cs
Here prog1.cs is the name of the program. If you compile your program which has unsafe code without
using the /unsafe
switch then compiler gives error.