XNA Game-Themed CS1 Examples (XGC1) Release 2.0 (XNA V3.1) 2/8/2010
Topic: Topic.6.Arrays
Example: Ex_9.CopyAndParameter

# Arrays: Passed as a Parameter; Copying arrays

References:

• Pre-requisite: it is assumed that you have read through the prior tutorials, and are familiar with the concepts covered in those tutorials.

Goals:

• In this tutorial, we will:
• Examine how to pass an array to a function
• Examine how to return an array from a function

1. Obtain the example code

Download and unzip the zip file and you will see an ExampleProgram folder. Open the ExampleProgram folder, the EXE folder contains the compiled program and you can double click on the .sln file to work with the source code.

When the game starts, you'll see a screen that looks similar to this:

In this tutorial, the program that you can run closely resembles the program that you ran in the previous tutorial.  What's different is the message at the top of the screen.  The program displays the sum of all the hit counts as "total=341", followed by the sum of one half of all the hit counts (in this case, 168).  Similarly, the average is calculated on all the hits, and then calculated on the halved copy of the number of hits.  Likewise, in the above picture, the minimum number of hits is 6, and the minimum of the halved number of hits is 3, while the maximum is 116, and maximum of the halved numbers is 58.

The two major topics for this tutorial are

1. Passing an array to a function, and

2. Creating a copy of an array within a function, and returning that copy back to the calling function

A minor change to the game is that the blocks are now initialized so that they start with -1 hits (instead of zero hits).

2. Examining The Program: Passing An Array To A Function (As A Parameter)

Let's examine the C# source code that produces the behavior we see on-screen, starting with the code that's specific to passing arrays as parameters (to functions).

• Declaring the instance variables and named constants, and initialize them protected override void InitializeWorld() {     // allocate memory for number of hits per each block     m_BlockHits = new int[ARRAY_SIZE];     InitializeBlockHits(m_BlockHits);     #region Allocate memory and initialize the block array     m_Blocks = new XNACS1Rectangle[m_BlockHits.Length];
• For this tutorial, we don't need to declare any new instance variables, nor any new named constants.  However, we have added a new function to help with the initialization of the array, named InitializeBlockHits.  Since C# will fill the array with zeros for us, we will pick an arbitrary number with which to initialize all the elements of the array: we've chosen -1 for this tutorial.
• What's new is that we're now passing in a reference to the m_BlockHits array, as a parameter, to each of this new function:
InitializeBlockHits(m_BlockHits);
• You'll notice that we're passing in a reference to the m_BlockHits array, even though we didn't use the ref keyword.  This is because arrays are always passed by reference.
• (Yes, you can by a reference to an array by reference, using the ref keyword on your array parameter.  The semantics of this are beyond this tutorial, and should probably be avoided until you fully understand how this actually works)
• As was mentioned previously, arrays are passed by reference, even though we didn't use the ref keyword anywhere.  What this means is that if you modify the elements of the array, those changes will be visible to the calling function.
• Contrast this with 'simple' types, such as integers, where you could modify the parameter and not have to worry that some other part of the program would see the new, changed value that you'd assigned to the variable.
• We're taking advantage of the fact that arrays are passed by reference for our initialization function - we pass the array (by reference) to InitializeBlockHits, which then changes all the values in the array.  The rest of the program then sees these new values, since InitializeBlockHits was given a references to the same array that the rest of the program uses.
• InitializeBlockHits /// /// Set each element of the provided array to a starting value. /// Right now, that starting value is -1, although you could change it. /// /// The array to initialize private void InitializeBlockHits(int[] intArray) {     // initialize each hits to be -1     for (int i = 0; i < intArray.Length; i++)         intArray[i] = -1; }
• The first think you'll notice is that the function has a parameter, and that the parameter is an array:
private void InitializeBlockHits(int[] intArray)
• Notice that syntax-wise, passing an array is just like passing any other type of variable.  You list the type (int[]), then the name (intArray).
• Notice that in the calling function (InitializeWorld, in this case), we call the function like normal, and list the name of the array:
InitializeBlockHits(m_BlockHits);
• When passing an array as a parameter, we do NOT use brackets at all.  We want to pass the ENTIRE ARRAY, not just a single element.
• Once the function has begun, we can see that it's using the normal 'traverse the entire array' pattern to iterate through all elements in the array.  For each element, we assign it the value -1:
for (int i = 0; i < intArray.Length; i++)

intArray[i] = -1;

• Notice that because we're using the .Length property of the array, we don't need to pass any extra parameters into the function in order to know what the length of the array is.
• UpdateWorld(): #region Similar to previous example, only difference: array stat functions take parameters    // collect stats of hits by calling the new array stat functions     int total = SumOfArray(m_BlockHits);     // this was: TotalHits();     float avg = AverageOfArray(m_BlockHits); // this was: AverageHits();     int min = MinInArray(m_BlockHits);       // this was: MinHits();     int max = MaxInArray(m_BlockHits);       // this was: MaxHits();     #endregion
• UpdateWorld for this tutorial is almost identical to the UpdateWorld for the one for the previous tutorial, down to the name of the temporary, local variables that we're using here.
• Each of the four methods have been similarly modified, so let's examine the first one in detail.
• SumOfArray(): /// /// Compute total number of hits by adding up the contents /// of all intArray content. /// /// an integer array /// Sum of all elements in intArray private int SumOfArray(int[] intArray) {     int total = 0;     for (int i = 0; i < intArray.Length; i++)         total = total + intArray[i];     return total; }
• Above is the code for the SumOfArray method, which sums up all the total numbers in the intArray array.   This is almost identical to the TotalHits method from the previous tutorial.  The only substantive change is that the method is now given an array to work as a parameter, rather than only working on one, specific array identified by an instance variable.
• For 'Phase 2', we use a loop to iterate over every element in the array:
for (int i = 0; i < intArray.Length; i++)

total = total + intArray[i];

• You'll notice that because we're using the provided parameter, our code access whichever array intArray refers to (instead of always using the m_BlockHits array)
• AverageOfArray, MinInArray, and MaxInArray have been similarly modified.

3. Examining The Program: Returning An Array From A Function

Let's examine the C# source code that produces the behavior we see on-screen, starting with the code that's specific to creating a copy of the hits array, and returning it.

You'll notice that when the program runs, the game displays (essentially) two sets of numbers across the top.  The second set of numbers is calculated using a copy of the the m_BlockHits array, except that in the copy, all the numbers are halved.  We accomplish this by creating another array that is a copy of the m_BlockHits array, going through, and halving the value of each element in the array.

You will also notice that because our four functions (SumOfArray, AverageOfArray, MinInArray, and MaxInArray) all accept an array as a parameter, this means that we can reuse those functions again here.  This is much better than creating a duplicate set of nearly-identical functions that do almost the same thing except on a different array.

• UpdateWorld(): #region make a copy of m_BlockHits array, and compute the stats for the new array    int[] newArray = CopyArray(m_BlockHits);         // Just for fun, half all the values in the newArray     for (i = 0; i < ARRAY_SIZE; i++)         newArray[i] = newArray[i] / 2; // notice this is integer division     // now: compute the stats. for the new array     int newTotal = SumOfArray(newArray);     float newAvg = AverageOfArray(newArray);     int newMin = MinInArray(newArray);     int newMax = MaxInArray(newArray);     #endregion
• This portion of UpdateWorld starts by calling the new CopyArray function, which we'll see in more detail shortly.
int[] newArray = CopyArray(m_BlockHits);
• You'll notice that this function both takes an array parameter (m_BlockHits), AND it returns a new array.  In order to use that new array, we need to keep track of it using a variable.  In this case, we keep track of the new array using the local variable newArray.
• CopyArray will return an exact duplicate of the array that we gave it - the copy has the same number of elements, and each element has the same value as the original.  In order to make it clear that we are, in fact, dealing with a different array, we choose to go through and halve each element of the array:
// Just for fun, half all the values in the newArray

for (i = 0; i < ARRAY_SIZE; i++)

newArray[i] = newArray[i] / 2; // notice this is integer division

• After that, the program then calculates the sum and average of the new values in the array, as well as the finding the minimum and maximum values in the new array, using the exact same functions as we used for the original array.  This is possible because the functions are told which array to operate on via the parameter that we pass to it.  In this case, we're passing newArray, instead of m_BlockHits.
• After this, UpdateWorld finishes by displaying the computed values for the original array, and the halved array at the top of the screen.
(This code is not included above)
• CopyArray(): /// /// creates a new array, copy from srcArray and return the new array /// /// Input source array to be copied. /// Size of the input array. /// A copy identical to srcArray private int[] CopyArray(int[] srcArray) {     int[] dstArray = new int[srcArray.Length];     for (int i = 0; i < srcArray.Length; i++)         dstArray[i] = srcArray[i];     return dstArray; }
• Using the 'Three Phase' description explained in the previous tutorial, we can see how CopyArray works.  For the purposes of this example, let's assume that when CopyArray starts, srcArray contains the following values:
Variable Name Value
srcArray an array with these values: Index 0 1 2 3 4 Value 11 107 18 11 116

• In 'Phase 1', CopyArray creates a new array, whose length is the same as the "source array" (srcArray) that was passed in as a parameter.  We assign the reference to that new array to the variable dstArray (for "destination array")
• Because C# automatically puts zeros into each element of the brand-new array, this line will produce the following result:
Variable Name Value
srcArray an array with these values: Index 0 1 2 3 4 Value 11 107 18 11 116

dstArray an array with these values: Index 0 1 2 3 4 Value 0 0 0 0 0

• This is somewhat close to what we want (the arrays both contain integers, and are the same length), but dstArray isn't an exact copy of srcArray (the elements of dstArray do not contain the same values as the corresponding elements of the srcArray).
• In 'Phase 2', CopyArray iterates through both arrays, copying numbers from the source array (srcArray) to the destination array (dstArray).  Let's trace a couple iterations to see how this works out:
1. In the first iteration of the loop, i is assigned the value zero, which is less than the length of the source array.  As a result, the line
dstArray[i] = srcArray[i];

is executed, which copies the 11 in srcArray[0] into element 0 of dstArray.  The following table shows the values of the various variables, BEFORE the i++ has been executed:

Variable Name Value
srcArray an array with these values: Index 0 1 2 3 4 Value 11 107 18 11 116

dstArray an array with these values: Index 0 1 2 3 4 Value 11 0 0 0 0

i 0

You'll notice that at this point, the green 'Already Done' region of the arrays consists of just element 0.  In the next iteration, the blue 'Current Element' region will be element #1 of both arrays, with the 'Future Work' region consisting of elements #2 and higher.

2. In the next  iteration of the loop, i is incremented to the value of 1, which is less than the length of the source array.  As a result, the line
dstArray[i] = srcArray[i];

is executed, which copies the 107 in srcArray[1] into element 1 of dstArray.  The following table shows the values of the various variables, BEFORE the i++ has been executed:

Variable Name Value
srcArray an array with these values: Index 0 1 2 3 4 Value 11 107 18 11 116

dstArray an array with these values: Index 0 1 2 3 4 Value 11 107 0 0 0

i 1

You'll notice that at this point, the green 'Already Done' region of the arrays consists of elements #0 and #1.  In the next iteration, the blue 'Current Element' region will be element #2 of both arrays, with the 'Future Work' region consisting of elements #3 and higher.

3. In the next  iteration of the loop, i is incremented to the value of 2, which is less than the length of the source array.  As a result, the line
dstArray[i] = srcArray[i];

is executed, which copies the 18 in srcArray[2] into element 2 of dstArray.  The following table shows the values of the various variables, BEFORE the i++ has been executed:

Variable Name Value
srcArray an array with these values: Index 0 1 2 3 4 Value 11 107 18 11 116

dstArray an array with these values: Index 0 1 2 3 4 Value 11 107 18 0 0

i 1

You'll notice that at this point, the green 'Already Done' region of the arrays consists of elements #0, #1 and #2.  In the next iteration, the blue 'Current Element' region will be element #3 of both arrays, with the 'Future Work' region consisting of elements #4 and higher.

4. The CopyArray continues to follow this pattern of execution until all values have been copied from srcArray into dstArray.
• When the loop has finished, all values have been copied from srcArray to dstArray, and so for this function, there really isn't a 'Phase 3'.  After this, the only thing left is to send the new array back to the calling function:
return dstArray;
• Once again we use the name of the array, but NOT any brackets - we want to send the whole array back, not just a single element.

FURTHER EXERCISES:

1. Start from a blank starter project (1000.201, if you need it), and re-do the code from memory as much as possible.  On your first try, do what you can, and keep the above code open so that when you get stuck, you can quickly look up what you forgot (and that after you finish a line, so that you can compare your line to the 'correct' line).  On the next try, do the same thing, but try to use the finished code less.  Repeat this until you can type everything, without referring to the tutorial's code.
• Repeat this exercise daily for several days, so that you really get the hang of this.  As you go on, periodically review this by re-doing this exercise.
2. Examining The AvergeOfArray() Function
For this exercise, you should use the same project that was explained in the above tutorial.
Notice that the numbers stored in the intArray array are integers, and that SumOfArray returns an integer.  Notice also that we are specifically declaring total to be a float, not an int.  For the version of the function (which is subtly different from the AverageHits in the previous tutorial), is it strictly necessary to declare total to be a float?
3. Examining The CopyArray() Function
For this exercise, you should use the same project that was explained in the above tutorial.
In UpdateWorld, when the program first calculates the sum, average, etc on the original array, and then goes back and calculates these values for the new, 'halved' array.   Why are all the values in the 'halved' array half of what they are in the original, rounded down?
4. Creating a IncrementAll function
For this exercise, you should use the same project that was explained in the above tutorial.
For this exercise, you should create a function named IncrementAll, which will be given an integer array as a parameter, and will go through and increase the value of each element by one.  Try modifying your game so that UpdateWorld calls this function.