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
/* 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.
