29.7. Using the * Indirection operator

Let's look at the third use of the * symbol in C#. You have seen * used:

  • as a binary multiplication operator (e.g. int x = 3*4;);

  • to denote a pointer referent type during declaration of a pointer type (e.g. long* pTemp; declares a pointer variable called pTemp of referent type long).

When applied on a single operand in unsafe codes, the * symbol becomes the indirection [14] operator.

[14] Indirection is also commonly called 'dereferencing'. These two terms are often used interchangeably in C/C++ literature.

The indirection operator is used to obtain the value of the variable to which the pointer points. It can only be applied to a pointer type with the exception of void*. Applying it to a non-pointer type (such as a managed type) or to the void* pointer type results in a compilation error. For example, if pInt is a pointer, *pInt will mean 'the value stored at the address stored in the pointer pInt'.

Let's continue our example where we left off:

10: int myInt = 3;
11: int* pInt = &myInt;
12: int yourInt = *pInt;
					

In the third statement (line 12), a new int variable is declared and called yourInt, and has assigned to it the value stored at the address location stored in pInt. Now, pInt is storing the address 990A 16. Going over to that address retrieves the value stored there (which is 3). Since pInt is of referent type int, it is supposed to store the address of an int, and that tells the compiler to retrieve only four bytes from 0x990A.

The statement on line 12 results in the memory map shown in Figure 29.5.

Figure 29.5. The final memory map.


The expression *pInt means:

  • get the value stored in pInt (=0x990A);

  • go to that address;

  • get the value stored at this address. The number of bytes to consider in determining the value depends on the referent type of pInt. (Since pInt is of type int*, only four bytes will be taken from 0x990A to 0x990D)

  • return this value (which is 3).

Having gone through both the indirection pointer and address-of operator, it's time to see a full example. Study the following program.

 1: using System;
 2:
 3: public class TestClass{
 4:
 5:   public unsafe static void Main(){
 6:     double d1 = 3.45;
 7:     double* pD = &d1;
 8:     double d2 = *pD;
 9:
10:     Console.WriteLine("d1 :"+ d1);        <-- 1
11:     Console.WriteLine("&d1 :"+ (int)&d1);        <-- 2
12:
13:     Console.WriteLine("pD :"+(int)pD);        <-- 3
14:     Console.WriteLine("&pD :"+(int)&pD);        <-- 4
15:
16:     Console.WriteLine("d2 :"+ d2);        <-- 5
17:     Console.WriteLine("&d2 :"+ (int)&d2);        <-- 6
18:   }
19: }

(1)Value stored in dl: 3.45

(2)Address of d1:1243312

(3)Value stored in pD:1243312

(4)Address of pD: 1243320

(5)Value stored in d2: 3.45

(6)Address of d2: 1243328

If you are using csc.exe, you must compile any source file containing unsafe codes with the /unsafe flag:

c:expt>csc /unsafe test.cs

Output: [15]

[15] The output may vary depending on the actual address allocated to the variables by your .NET runtime.

C:expt>test
d1  :3.45
&d1 :1243312
pD  :1243312
&pD :1243320
d2  :3.45
&d2 :1243328

Here is another example. This time, a pointer is declared to a user-defined struct called Temp.

 1: using System;
 2:
 3: public class TestClass{
 4:
 5:   public struct Temp{        <-- 1
 6:     public int a;
 7:     public int b;
 8:   }
 9:
10:   public unsafe static void Main(){
11:     Temp t1 = new Temp();
12:     t1.a = 1;
13:     t1.b = 1;
14:
15:     Console.WriteLine("t1.a : " + t1.a); // 1
16:
17:     Temp* pTemp = &t1;
18:     Temp t2 = *pTemp;
19:
20:     t2.a = 9;
21:     Console.WriteLine("t1.a : " + t1.a); // 1
22:     Console.WriteLine("t2.a : " + t2.a); // 9
23:
24:     int* pA = &t1.a;
25:     int myInt = *pA;
26:     Console.WriteLine("myInt: " + myInt); // 1
27:   }
28: }

(1)Unmanaged struct type

Output:

c:expt>test
t1.a : 1
t1.a : 1
t2.a : 9
myInt: 1

Note that the statement on line 18 makes a copy of t1, so that t1 and t2 are stored at unique addresses. Changing t2.a (line 20) will not affect t1.a.

On line 24, a new int pointer is created which stores the address of t1's int field a. This is perfectly legal.

Remember that a struct containing a managed type is no longer considered an unmanaged type itself. In the code fragment below, NonPointerStruct is no longer considered to be unmanaged because it contains a AnotherStruct field. AnotherStruct contains a reference to object, a managed type. An unmanaged struct may not contain any reference to any managed type at any level of nesting.

10: public struct NonPointerStruct {        <-- 1
11:   int a;
12:   int b;
13:   AnotherStruct another;
14: }
15:
16: public struct AnotherStruct {       <-- 1
17:   object o;        <-- 2
18: }
19:
20: public unsafe static void Main(){
21:   NonPointerStruct* pNps;
22: }

(1)Managed struct types

(2)Field of managed type

Compilation error:

c:expt>csc /unsafe test.cs
test.cs(21,20): error CS0208: Cannot take the address or
size of a variable of a managed type
('TestClass.NonPointerStruct')

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.30.236