Introduction
I guess one of the reasons a lot of C programmers could not digest Java was
because Java didn't let them manipulate pointers. Anyway C# supports pointers.
You can use the unsafe
keyword to inform the compiler that the following function
or block is unsafe. And once you do that you can use pointers in that unsafe
area.
Program 1
using System;
class nish
{
unsafe static void Increment(int* p)
{
*p=*p+1;
}
public static void Main()
{
int i = 1;
unsafe
{
Increment(&i);
}
Console.WriteLine (i);
}
}
When you run the program, you'll see 2 printed on screen. This is because you
have passed the address of the variable i to the function Increment. Variable i
is created on the stack. &i gives its address on the stack. Thus when within
the function Increment, p is pointing to i's address. Thus when we add 1 to *p
we are actually incrementing i;
Program 2
The following program should make things clearer to you.
using System;
class nish
{
unsafe public static int Main()
{
int j=100;
int k=100;
Console.WriteLine("address of j={0} and address of k={1}",(int)&j,(int)&k);
Console.WriteLine("j={0} k={1}",j,k);
int *p;
p=&j;
Console.WriteLine("p now points to {0}",(int)p);
*p=200;
Console.WriteLine("j={0} k={1}",j,k);
p=&k;
Console.WriteLine("p now points to {0}",(int)p);
*p=300;
Console.WriteLine("j={0} k={1}",j,k);
return 0;
}
}
When I ran it I got the below output. You would get something similar. The
output will clearly give an idea of what's going on.
address of j=1244312 and address of k=1244308
j=100 k=100
p now points to 1244312
j=200 k=100
p now points to 1244308
j=200 k=300
First we assign the address of j to p. Thus when we change *p we
automatically change j. Then we change p to point to k's address and thus when
we change *p we are actually changing k
Also remember than the pointer variable p itself has an address. This
following program will make that clear.
Program 3
using System;
class nish
{
public static void Main()
{
unsafe
{
int a=100;
int *p;
p=&a;
Console.WriteLine("address of a is {0}",(int)&a);
Console.WriteLine("p now points to {0}",(int)p);
Console.WriteLine("address of the pointer variable p is {0}",(int)&p);
}
}
}
On running it I got the below output. You'll get similar output too. Notice
the alternate use of the unsafe
keyword
address of a is 1244312
p now points to 1244312
address of the pointer variable p is 1244308
In my case 1244308 is the address of the pointer variable p. The contents of
that address is 1244312 which is an address. Thus when we take *p we are referring
to the address 1244312. Try printing out *p and it will print out the contents
of 1244312 which happens to be 100 in the above case. I hope things are clearer
now.
Program 4
Okay. In this final program I'll show you how to use pointers to manipulate a
character string. In this program there is a function that takes a string and
encodes or decodes it using XOR. If you pass a string to it it will encode it
and if you pass the encoded string to it, it will decode it. Of course this is
by no way a safe or practical encryption method. I am using it merely to
demonstrate the use of pointers.
using System;
class nish
{
public static void Main()
{
string s="Code Project is cool";
Console.Write("the original string : ");
Console.WriteLine("{0}\r\n",s);
char[] b = new char[100];
s.CopyTo(0,b,0,20);
Console.Write("the encoded string : ");
unsafe
{
fixed(char *p=b)NEncodeDecode(p);
}
for(int t=0;t<20;t++)
Console.Write(b[t]);
Console.WriteLine("\r\n");
Console.Write("the decoded string : ");
unsafe
{
fixed(char *p=b)NEncodeDecode(p);
}
for(int t=0;t<20;t++)
Console.Write(b[t]);
Console.WriteLine();
}
unsafe public static void NEncodeDecode(char *s)
{
int w;
for(int y=0;y<20;y++)
{
w=(int)*(s+y);
w=w^5;
*(s+y)=(char)w;
}
}
}
This is the output I got. You should get this too.
the original string : Code Project is cool
the encoded string : Fja`%Uwjo`fq%lv%fjji
the decoded string : Code Project is cool
You will notice a new keyword here called fixed
. When you
precede a statement or a function with fixed
you are
instructing the .Net garbage collector not to relocate that variable till
the statement or function has finished. The fixed
keyword is only
allowed in an unsafe context. If we don't use fixed
there is little point in
using pointers as the results would be unpredictable if the garbage collector
keeps relocating the managed variables. Luckily the compiler won't let you point
to managed variables unless you specify the fixed
keyword.
In the function you can see that I use the expression *(s+y). s is the
address pointing to the string. y is incremented starting from 0 and ending upto
19. Thus when I give *(s+y) I get the pointers to characters in those locations.
Lets assume s points to 1000. Thus *(s) will give me the contents of location
1000. Now if I give *(s+1) I get the contents of 1002, and for *(s+2) I get 1004
and so on. The compiler knows I am pointing to a character array and thus each
increment jumps 2 bytes as char is a 16 bit value. C# actually maps char to the
.Net type System.Char.
Program 5
Sometimes, you might need to call the Win32 API. And there are API functions,
quite a good number of them to be precise, that take pointers as arguments and
often return data via pointers. The following program is an example where such a
call is made. Of course, there are other means to obtain the Windows folder from
.NET, but the point of the program is to demonstrate the use of unsafe blocks in
such a scenario where we need to call an API function that expects pointers.
using System;
using System.Text;
using System.Runtime.InteropServices;
class Class1
{
[DllImport("kernel32", SetLastError=true)]
static extern unsafe uint GetWindowsDirectory(byte* lpBuffer,uint uSize);
static void Main(string[] args)
{
byte[] buff = new byte[512];
unsafe
{
fixed(byte *pbuff=buff)GetWindowsDirectory(pbuff,512);
}
ASCIIEncoding ae = new ASCIIEncoding();
System.Console.WriteLine(ae.GetString(buff));
}
}
Conclusion
Please be careful when writing unsafe code. Any small error, even a silly
typing mistake, might crash your program randomly and unpredictably. The error
might also be a rarely occurring one and thus harder to debug. But those of you
who had used pointers in C/C++ and want to use them in C# may do so at their
leisure. Thank You.