Arrays

I was quite surprised and pleased when I first started playing with arrays in IL. I'd read lots of documentation for managed high-level languages that told me over and over again that arrays were nothing more than instances of the System.Array class - and I'd vaguely gathered from that that arrays must be represented only by that class and there was no in-built support for them. I couldn't have been more wrong! It's true that if you want to do something fancy, such as have a VB6-style array that isn't zero-indexed, then you're on your own and will need to work by explicitly invoking System.Array methods, but if you just want a simple zero-indexed one-dimensional array (known in metadata as szarray and more generally as a vector), then you will find considerable in-built support through several specific IL commands.

Arrays are declared using a similar syntax as for C#, using square brackets. For example, a one-dimensional array of int32s would be denoted int32[], while a two-dimensional rectangular array of objects would be denoted object [,]. Note, however, that for multi-dimensional arrays, beyond the availability of this syntax there is little in-built support in IL. Again, you're best off explicitly manipulating System.Array methods.

For the rest of this section we'll concentrate on manipulating vectors. The IL commands available to manipulate vectors are as follows:

Instruction

Meaning

newarr

Instantiates an array (vector) object

ldelem.*

Loads a given element of an array onto the evaluation stack

ldelema.*

Loads the address of the given element of an array onto the evaluation stack

stelem.*

Pops the top element of the evaluation stack into the given array element

ldlen

Loads the length of an array onto the evaluation stack

Somewhat confusingly, although zero-indexed one-dimensional arrays are technically known as vectors, the instruction names and surrounding documentation continues to refer to them as arrays. For consistency, I'll follow the same practice here.

newarr works much like newobj, except that it instantiates an array. The type of each element of the array is supplied in the argument to newarr. Like newobj, it pushes a reference to the array onto the stack:

Important 

..., no. of elements ..., array ref

ldelem.* expects the stack to contain a reference to the array, and the index of the element. It pushes a copy of the element (or of the reference if the element type is a reference type) onto the stack:

Important 

..., array ref, index ..., value

There are different ldelem.* instructions according to the data type of the element. For example, ldelem.i4 to retrieve elements of int32[] arrays, ldelem.il for int8[] arrays, and ldelem.ref for object[] arrays and arrays of reference types. The full list of ldelem.* instructions is in the appendix. ldelema works like ldelem.*, but loads the address of the element as a managed pointer instead of its value.

For each ldelem.* instruction there is a corresponding stelem.* instruction, such as stelem.i4, stelem.il, stelem.ref:

Important 

..., array ref, index, value ...

Finally, the ldlen instruction expects the top item on the stack to be an array reference, which it replaces with the length of the array:

Important 

..., array ref ..., length

We will use all these instructions (apart from ldelema) in the following sample, which illustrates the use of arrays. It sets up an array of int32 of length 10. It then populates the array by setting element 0 to 0, element 1 to 4, element 2 to 8, and so on. Finally, it displays the value of element 3 (12), and the length of the array (10).

The code is quite long, so we'll present it and then go over it in some detail:

   .method static void Main() cil managed   {      .maxstack 10      .locals init (int32 counter, int32[] thearray)      .entrypoint      // Set up array      ldc.i4.s 10      dup      stloc.0      newarr    int32      stloc.1 Loop:      // Test if we have counted down to zero yet      ldloc.0      brfalse.s Finish      // Decrement counter      ldloc.0      ldc.i4.1      sub      stloc.0      ldloc.1      ldloc.0      // Get stack ready to store element      dup      ldc.i4.4      mul      stelem.i4      br.s      Loop Finish:      // Display element no. 3 of array      ldstr     "Element no. 3 is "      call      void [mscorlib]System.Console::Write(string)      ldloc.1      ldc.i4.3      ldelem.i4      call      void [mscorlib]System.Console::WriteLine(int32)      // Display length of array      ldstr     "Length of array is "      call      void [mscorlib]System.Console::Write(string)      ldloc.1      ldlen      call      void [mscorlib]System.Console::WriteLine(int32)      ret   } 

Other than the use of the array commands we've just described, there's little new in this code. However, its use of the evaluation stack is considerably more complex than anything we've done before - as evidenced by our .maxstack value of 4. This sample also includes our first use of the dup instruction. dup is really very simple - it simply duplicates the top item of the stack. It has a stack delta of +1, and after executing it the top two items of the stack will have identical contents. It's very useful if you want to store the top item of the stack but want to leave a copy of it on the stack as well.

The first part of the code deals with setting up the array. To make things clearer, we'll present the code as a table, showing the contents of the evaluation stack after executing each instruction. In the table, the symbol "array ref" denotes an object reference to the array:

Instruction

State of Evaluation Stack After Executing

Comments

ldc.i4.s 10

10

 

dup

10, 10

dup puts an extra 10 on the evaluation stack so we can store it in local 0

stloc.0

10

 

newarr int32

array ref

 

stloc.1

<EMPTY>

 

We first load the constant 10 onto the stack - this is how big we want the array to be.

Now for the loop. The loop will be executed quite a few times with different values for the count. We'll use "counter" in the table to indicate that value. Notice that we are counting down from 10 rather than up to 10. Counting down saves a local variable, makes for more compact code, and may give a slight improvement in performance since at native-executable level (rather than IL level) testing a value for zero is faster than comparing it to 10 (you have to subtract 10, then compare it to zero).

Instruction

State of Evaluation Stack After Executing

Comments

Loop:

counter

 

ldloc.0

  

brfalse.s Finish

<EMPTY>

If counter has reached zero we've finished and can go display the results

ldloc.0

counter

 

ldc.i4.1

counter, 1

 

sub

counter - 1

 

stloc.0

<EMPTY>

 

ldloc.1

array ref

 

ldloc.0

array ref, counter - 1

 

dup

array ref, counter - 1, counter - 1

 

ldc.i4.4

array ref, counter - 1, counter - 1, 4

 

mul

array ref, counter -1, 4* (counter - 1)

4 * (counter - 1) is the value to be stored in element (counter - 1)

stelem.i4

<EMPTY>

 

br.s Loop

<EMPTY>

 

Notice in this code that the stack is empty immediately after the brfalse.s statement, and on executing the final br.s statement. It's significant that the same number of items (zero) is on the stack at these points. If there were a net change, that would signal some invalid code. Suppose for example we'd done something wrong in the code, so that between the brfalse.s Finished and the br.s Loop statements, there was a net stack delta of one. That would mean that every time we go round the loop, the stack would have one more element than previously. The JIT compiler would refuse to compile such code because it would be unable to determine in advance the details of the types on the stack for each instruction (we wouldn't have been able to present a table like the one above - which amounts to the same thing). So quickly adding up the stack deltas on a loop provides a good check on your code.

We won't go over the final part of the code for the sample, which displays the results, as the use of the stack for that code is relatively straightforward.



Advanced  .NET Programming
Advanced .NET Programming
ISBN: 1861006292
EAN: 2147483647
Year: 2002
Pages: 124

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