CSC 161 Schedule Readings Labs & Projects Homework Deadlines Resources

Array Parameters

The concepts of passing arrays as parameters follows from the underlying philosophy of arrays in C. In order to understand array parameters, it helps to first review the mechanisms available for passing simple types (e.g., ints, doubles) to functions in C.

Array Parameters

In C, the declaration

double numberArray [5] = {43.7, 23.1, -56.2, 98.6, -40.0};

allocates space for 5 double precision numbers and initializes those values. The variable numberArray refers to the address of the first array element. From a compiler's perspective, a reference to the variable numberArr usually is equivalent to the expression &numberArray[0]. (Documentation lists three exceptions, as noted in the given link.)

Because numberArray gets interpreted as an address, parameter passage for arrays involves the base address of the array—without specifying an ampersand &, the base address of the array is passed to the function.

As an example, consider the following code:

/* illustration of passing an array parameter */
	    
#include <stdio.h>

/* Function that demonstrates an array parameter whose values are printed,
 *  modified, and printed again */
void
arrayFunction (double arrayParam[])
{
  int i;
  printf ("values of array at start of function: ");
  for (i = 0; i < 5; i++)
    printf ("%8.2lf", arrayParam[i]);
  printf ("\n");
  arrayParam[1] += 100;
  arrayParam[3] += 300;
  printf ("values of array at end of function:   ");
  for (i = 0; i < 5; i++)
    printf ("%8.2lf", arrayParam[i]);
  printf ("\n");
} // arrayFunction

/* Declare and initialize an array, pass to a function and print the result */
int
main (void) 
{
  double numberArray [5] = {43.7, 23.1, -56.2, 98.6, -40.0};

  arrayFunction (numberArray);

  printf ("values of array at end of main:       ");
  for (int k = 0; k < 5; k++)
    printf ("%8.2lf", numberArray[k]); 
  printf ("\n");

  return 0;
} // main

When this program is run, initial values are stored in the numberArray array. When function arrayFunction is called, the base address of the numberArray array is copied to the arrayParam parameter, but the values within the array are not copied. Thus, array references within the arrayFunction function refer to the original array—there is not a separate copy of the array.

The resulting output from this program follows:

values of array at start of function:    43.70   23.10  -56.20   98.60  -40.00
values of array at end of function:      43.70  123.10  -56.20  398.60  -40.00
values of array at end of main:          43.70  123.10  -56.20  398.60  -40.00

Note that the changes to array elements 2 and 4, made within the function, are recorded in the main array.

Observation

As a secondary observation, note that an array variable (e.g., numberArray) gives you information about where the array begins. However, the array does NOT provide the programmer information about how long it is or where it stops. Thus, in the sample program, the programmer had to remember that numberArray was declared with 5 elements, and this information was hard coded into the program in both arrayFunction and main.

If a function will be called with a variety of arrays given as the parameter value, it is common for an extra parameter (the array length) to be added, so the function will know how many array elements might be involved in processing.

Array lengths: #define versus const int

One negative aspect of the code above is that it involves a magic number for the array length that is the same throughout the program. This suggests we should be using a globally named value to refer to the array length. Without that, if we decide to store six numbers instead of five, the programmer needs to ensure they have found every use of the value 5 where it means the length of the array and replace it with a new value. This can be a fraught, error-prone process.

Up to this point, we have preferred to use the const keyword to declare constant values in our programs. That's because a type gets clearly associated with those names, and we can then rely on the compiler to use this information (often to keep the programmer from overlooking type-related mistakes).

However, in these readings we have intentionally made use of the preprocessor directive #define to denote the fixed length of an array. Why?

The difference is subtle, and ever-so-slightly beyond the scope of this course, but suffice it to say that the preprocessor changes your code before it gets to the compiler, leaving a literal constant value in place of the defined name. In other words, the following code

#define LENGTH 5
double array[LENGTH];
gets changed by the preprocessor so that the compiler only sees
double array[5];
The compiler can therefore allocate a fixed amount of space for the array in a stack frame where that variable is declared. If we instead write
const int LENGTH = 5;
double array[LENGTH];
the result is subtly different. Because the compiler can interpret LENGTH as a variable, the result may be what is called a variable length array (VLA). In these cases, the length of the array is not known at compile time so the stack frame size becomes dynamic, which can sometimes cause lower-level inefficiencies.