You need a generic
method that accepts two pointers and switches the addresses that each
pointer points to. In other words, if X
points to
an integer variable Foo
and Y
points to an integer variable Bar
, you want to
switch X
so that it points to
Bar
and switch Y
so that it
points to Foo
.
Create
a method that accepts two void
pointers. The
following method accepts two pointers to void
by
reference. The by reference is required since we are actually switching the
values contained in the pointer variables, not the value that the
pointer is pointing to:
public unsafe void Switch(ref void* x, ref void* y) { void* temp = x; x = y; y = temp; }
The following test code calls the Switch
method
with two integer variables that point to different memory locations:
public unsafe void TestSwitch( ) { int x = 100; int y = 20; int* ptrx = &x; int* ptry = &y; Console.WriteLine(*ptrx + " " + (int)ptrx); Console.WriteLine(*ptry + " " + (int)ptry); // Convert int* to void* void* voidx = (void*)ptrx; void* voidy = (void*)ptry; // Switch pointer values Switch(ref voidx, ref voidy); // Convert returned void* to a usable int* ptrx = (int*)voidx; ptry = (int*)voidy; Console.WriteLine(*ptrx + " " + (int)ptrx); Console.WriteLine(*ptry + " " + (int)ptry); }
The following is displayed:
100 1243108 20 1243104 20 1243104 100 1243108
The TestSwitch
method could just have easily been
written with another data type, such as a byte
,
shown here:
public unsafe void TestSwitch( ) { byte x = 100; byte y = 20; byte* ptrx = &x; byte* ptry = &y; Console.WriteLine(*ptrx + " " + (int)ptrx); Console.WriteLine(*ptry + " " + (int)ptry); // Convert byte* to void* void* voidx = (void*)ptrx; void* voidy = (void*)ptry; // Switch pointer values Switch(ref voidx, ref voidy); // Convert returned void* to a usable byte* ptrx = (byte*)voidx; ptry = (byte*)voidy; Console.WriteLine(*ptrx + " " + (int)ptrx); Console.WriteLine(*ptry + " " + (int)ptry); }
All that had to be done is to change the int*
types to byte*
types.
A
void
pointer has no type and therefore cannot be
dereferenced, nor can pointer arithmetic be applied to this type of
pointer. A void
pointer does have one very useful
function, though; it can be cast to a pointer of any other type. A
void
pointer is also able to be cast to the
following other value types as well:
In the Switch
method, used in the Solution for
this recipe, we notice that it takes two parameters by reference of
type void*
. We are declaring that any pointer type
may be passed to these two parameters on this method. Once inside the
Switch
method, we can manipulate the value
contained in the void
pointers. However, since we
do not know the original type that the void*
was
cast from, we cannot dereference the void*
.
The one drawback to this technique is that before the
Switch
method is called in the
TestSwitch
method, the int*
or
byte*
pointers must be cast to a
void*
. When the Switch
method
returns, the void*
pointers must be cast back to
their original types before they may be used. The reason for this
casting is that we are passing the void*
pointers
by reference instead of by value.
We could pass the void*
pointers by value instead,
and simply switch the values pointed to, rather than the memory
locations pointed to, in the Switch
method. This
new SwitchValues
method would look something like
this:
public unsafe void SwitchValues(void* x, void* y) { void* temp = x; *x = *y; *y = *temp; }
Unfortunately, this code will not compile, since you cannot
dereference a void*
. The void*
must be cast to its original type before it can be dereferenced. To
do this, we must also pass along the type information to the
SwitchValues
method. This can become very
cumbersome, and it will reduce the genericity of this method as well.