29.7 Using the Indirection operator


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.

graphics/29fig05.gif

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') 


From Java to C#. A Developers Guide
From Java to C#: A Developers Guide
ISBN: 0321136225
EAN: 2147483647
Year: 2003
Pages: 221
Authors: Heng Ngee Mok

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net