C# supports pointers in a limited extent. A C# pointer is nothing but a variable that holds the memory address of another type. But in C# pointer can only be declared to hold the memory address of value types and arrays. Unlike reference types, pointer types are not tracked by the default garbage collection mechanism. For the same reason pointers are not allowed to point to a reference type or even to a structure type which contains a reference type. We can say that pointers can point to only unmanaged types which includes all basic data types, enum types, other pointer types and structs which contain only unmanaged types.
Declaring a Pointer type
The general form of declaring a pointer type is as shown below,
type *variable_name;
Where * is known as the de-reference operator. For example the following statement
int *x ;
Declares a pointer variable x, which can hold the address of an int type. The reference operator (&) can be used to get the memory address of a variable.
int x = 100;
The &x gives the memory address of the variable x, which we can assign to a pointer variable
int *ptr = & x;.
Console.WriteLine((int)ptr) // Displays the memory address
Console.WriteLine(*ptr) // Displays the value at the memory address.
Unsafe Codes
The C# statements can be executed either as in a safe or in an unsafe context. The statements marked as unsafe by using the keyword unsafe runs outside the control of Garbage Collector. Remember that in C# any code involving pointers requires an unsafe context.
We can use the unsafe keyword in two different ways. It can be used as a modifier to a method, property, and constructor etc. For example
// Author: [email protected]
using System;
class MyClass
{
public unsafe void Method()
{
int x = 10;
int y = 20;
int* ptr1 = &x;
int* ptr2 = &y;
Console.WriteLine((int)ptr1);
Console.WriteLine((int)ptr2);
Console.WriteLine(*ptr1);
Console.WriteLine(*ptr2);
}
}
class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc.Method();
}
}
The keyword unsafe can also be used to mark a group of statements as unsafe as shown below.
// Author: [email protected]
//unsafe blocks
using System;
class MyClass
{
public void Method()
{
unsafe
{
int x = 10;
int y = 20;
int* ptr1 = &x;
int* ptr2 = &y;
Console.WriteLine((int)ptr1);
Console.WriteLine((int)ptr2);
Console.WriteLine(*ptr1);
Console.WriteLine(*ptr2);
}
}
}
class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc.Method();
}
}
Pinning an Object
The C# garbage collector can move the objects in memory as it wishes during the garbage collection process. The C# provides a special keyword fixed to tell Garbage Collector not to move an object. That means this fixes in memory the location of the value types pointed to. This is what is known as pinning in C#.
The fixed statement is typically implemented by generating tables that describe to the Garbage Collector, which objects are to remain fixed in which regions of executable code. Thus as long as a Garbage Collector process doesn't actually occur during execution of fixed statements, there is very little cost associated with this. However when a Garbage Collector process does occur, fixed objects may cause fragmentation of the heap. Hence objects should be fixed only when absolutely necessary and only for the shortest amount of time.
Pointers & Methods
The points can be passed as an argument to a method as showing below. The methods can also return a pointer.
// Author: [email protected]
using System;
class MyClass
{
public unsafe void Method()
{
int x = 10;
int y = 20;
int* sum = swap(&x, &y);
Console.WriteLine(*sum);
}
public unsafe int* swap(int* x, int* y)
{
int sum;
sum = *x + *y;
return ∑
}
}
class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc.Method();
}
}
Pointers & Conversions
In C# pointer types do not inherit from object and no conversion exists between pointer types and objects. That means boxing and un-boxing are not supported by pointers. But C# supports conversions between the different pointer types and pointer types and integral types.
C# supports both implicit and explicit pointer conversions within un-safe context. The implicit conversions are
- From any type pointer type to void * type.
- From null type to any pointer type.
The cast operator (()) is necessary for any explicit type conversions. The explicit type conversions are
- From any pointer type to any other pointer type.
- From sbyte, byte, short, ushort, int, uint, long, ulong to any pointer type.
- From any pointer type to sbyte, byte, short, ushort, int, uint, long, ulong types.
For example
char c = 'R';
char *pc = &c;
void *pv = pc; // Implicit conversion
int *pi = (int *) pv; // Explicit conversion using casting operator
Pointer Arithmetic
In an un-safe context, the ++ and - operators can be applied to pointer variable of all types except void * type. Thus for every pointer type T* the following operators are implicitly overloaded.
T* operator ++ (T *x);
T*operator -- (T *x);
The ++ operator adds sizeof(T) to the address contained in the variable and - operator subtracts sizeof(--) from the address contained in the variable for a pointer variable of type T*.
In an un-safe context a constant can be added or subtracted from a pointer variable. Similarly a pointer variable can be subtracted from another pointer variable. But it is not possible to add two pointer variables in C#.
In un-safe context = =, ! =, <, >, < =, > = operators can be applied to value types of all pointer types. The multiplication and division of a pointer variable with a constant or with another pointer variable is not possible in C#.
Stack Allocation
In an unsafe context, a local declaration may include a stack allocation initialiser, which allocates memory from the call stack.
The stackalloc T[E] requires T to be an unmanaged type and E to be an expression of type int. The above construct allocates E * sizeof(T) bytes from the call stack and produces a pointer of the type T* to the newly allocated block. The E is negative, a System.OverFlowException is thrown. If there is not enough memory to allocate, a System.StackOverflowException is thrown.
The content of newly allocated memory is undefined. There is no way to implicitly free memory allocated using stackalloc. Instead all stack allocated memory block are automatically discarded once the function returns.
class Test
{
char *buffer =
}
Pointers & Arrays
In C# array elements can be accessed by using pointer notations.
// Author: [email protected]
using System;
class MyClass
{
public unsafe void Method()
{
int[] iArray = new int[10];
for (int count = 0; count < 10; count++)
{
iArray[count] = count * count;
}
fixed (int* ptr = iArray)
Display(ptr);
//Console.WriteLine(*(ptr+2));
//Console.WriteLine((int)ptr);
}
public unsafe void Display(int* pt)
{
for (int i = 0; i < 14; i++)
{
Console.WriteLine(*(pt + i));
}
}
}
class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc.Method();
}
}
Pointers & Structures
The structures in C# are value types. The pointers can be used with structures if it contains only value types as its members. For example
// Author: [email protected]
using System;
struct MyStruct
{
public int x;
public int y;
public void SetXY(int i, int j)
{
x = i;
y = j;
}
public void ShowXY()
{
Console.WriteLine(x);
Console.WriteLine(y);
}
}
class MyClient
{
public unsafe static void Main()
{
MyStruct ms = new MyStruct();
MyStruct* ms1 = &ms;
ms1->SetXY(10, 20);
ms1->ShowXY();
}
}