29.13 Using the fixed keyword


29.13 Using the fixed keyword

You have seen that pointer types can only be unmanaged types. You can have a pointer of a struct type as long as this struct does not contain any field with a managed type. It is illegal to declare a pointer of a class type because a class is a managed type.

For managed types, the garbage collector is free to shift the actual location of managed objects in memory as the runtime desires. This is usually done for optimizing memory use. As far as the developer or application user is concerned , it doesn't really matter where a particular managed object is stored, as long as the variable referencing that object is updated accordingly with the new address if a shift occurs. The garbage collection is unaware of any pointers, and where they point to.

Here comes the problem. Assume that you have a class containing an int field. It is perfectly alright to declare an int pointer and pass to it the address of this int field, as long as these statements are in an unsafe context. Because the field is part of the object, won't there be a problem when the garbage collector decides to move the object “ which is considered to be managed data? Won't that result in the pointer pointing to an invalid address, which probably contains values that are part of another object which was recently shifted over?

I have altered the program listed in section 29.8 by pulling out the variable a from within Main() so that it is now an instance field instead of a local variable (line 5 below) “ the reason for doing this will be explained later. This results in a compilation error.

 1: using System;  2:  3: class TestClass{  4:  5:  int a  =  1;  // field  6:  7:   static unsafe void PerformOp (int* pX){  8:     *pX = 99;  9:   } 10: 11:   public static unsafe void Main(){ 12: 13:     TestClass tc = new TestClass(); 14:     Console.WriteLine("a was :" + tc.a); 15: 16:  PerformOp (&tc.a);  // or PerformOp(tc->a); 17: 18:     Console.WriteLine("a is :" + tc.a); 19:   } 20: } 

Compilation error:

 c:\expt>csc /unsafe test.cs test.cs(16,16): error CS0212: You can only take the address of unfixed expression inside of a fixed statement initializer 

For the above code scenario, Main passes the address of instance field a to PerformOp (line 16). Imagine a garbage collection sweep being performed immediately after this statement, and before the first line in PerformOp actually executes. What happens if this garbage collection activity decides to relocate the object tc ? If this happens, when the first line of PerformOp executes it sets the value stored at a 's previous memory address to 99 ! This might or might not be disastrous depending on luck, but the point is that the outcome will be non-deterministic . And that is why the compiler gives a compilation error.

What is needed is some way of telling the .NET runtime not to relocate a particular managed object on the memory map, because you might have a pointer pointing to it “ this is where the fixed keyword comes in useful. It is used to fix a movable variable so that its address remains constant for the duration of the statement.

The fixed keyword is used like this:

 fixed (type* ptr = expr) {   // embedded statement } 

where type is void or any unmanaged type, ptr is a pointer name , and expr is an expression that is implicitly convertible to type* . The embedded statement is a block of code or a single executable statement. (Curly brackets are optional if there is only one embedded statement.) When the embedded statement(s) is being executed, the .NET runtime guarantees that it will not relocate expr .

Assuming that MyChar is an instance field, and DoSomething is a method that takes in a char pointer, here is an example of how fixed can be used:

 fixed (char* pChar = &MyChar)   DoSomething(pChar); 

In this case, for as long as DoSomething() is executing, the address of MyChar will not be changed, and it is alright for DoSomething() to contain codes that rely on this fact.

Local variables in an unsafe method are automatically fixed, so that the garbage collector never relocates them. Non-local fields will have to be specifically declared with fixed to prevent relocation. [17]

[17] This is the reason why I made a a field variable instead of a local variable in the code example on page 398. If a remains a local variable, there is no need for it to be fixed .

Let's alter the program above so that you are assured of consistent results. This time there is no compilation error.

 1: using System;  2:  3: class TestClass{  4:  5:   int a = 1; // field  6:  7:   static unsafe void PerformOp (int* pX){  8:     *pX = 99;  9:   } 10: 11:   public static unsafe void Main(){ 12: 13:     TestClass tc = new TestClass(); 14:     Console.WriteLine("a was :" + tc.a); 15: 16:  fixed(int* p  =  &(tc.a))  17:       PerformOp (p); 18: 19:     Console.WriteLine("a is :" + tc.a); 20:   } 21: } 

Output:

 c:\expt>test a was :1 a is :99 

In this case, when PerformOp is invoked (on line 17), the .NET runtime will not relocate tc.a , and the value of 99 will be assigned to the address intended (on line 8).

Things may become inefficient when an object is fixed, since the garbage collector and the .NET runtime cannot move it around in memory to optimize it. Fixed objects may hence lead to memory fragmentation [18] “ a good rule of the thumb is to fix objects only when necessary.

[18] Memory fragmentation is somewhat similar to disk fragmentation, except that it now happens to bytes in memory instead of to clusters on a disk.



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