Working with Functions in C: Functions and Arrays

As with ordinary variables and values, it is also possible to pass the value of an array ele- ment and even an entire array as an argument to a function. To pass a single array ele- ment to a function (which is what you did in Chapter 7 when you used the printf function to display the elements of an array), the array element is specified  as an argu- ment to the function in the normal fashion. So, to take the square root of averages[i] and assign the result to a variable called sq_root_result,a statement such as

sq_root_result = squareRoot (averages[i]);

does the trick.

Inside the squareRoot function itself, nothing special has to be done to handle single array elements passed as arguments.  In the same manner as with a simple variable, the value of the array element is copied into the value of the corresponding formal parame- ter when the function is called.

Passing an entire array to a function is an entirely new ball game. To pass an array to a function, it is only necessary to list the name of the array, without any subscripts, inside the call to the function. As an example, if you assume that gradeScores has been declared as an array containing 100 elements, the expression

minimum (gradeScores)

in effect passes the entire 100 elements contained in the array gradeScores to the func- tion called minimum. Naturally, on the other side of the coin, the minimum function must be expecting an entire array to be passed as an argument and must make the appropriate formal parameter declaration. So the minimum function might look something like this:

int minimum (int values[100])

{

return minValue;

}

The declaration defines the function minimum as returning a value of type int and as taking as its argument an array containing 100 integer elements. References made to the formal parameter array values reference the appropriate elements inside the array that was passed to the function. Based upon the function call previously shown and the cor- responding function declaration, a reference made to values[4], for example, would actually reference the value of gradeScores[4].

For your first program that illustrates a function that takes an array as an argument, you can write a function minimum to find the minimum value in an array of 10 integers. This function, together with a main routine to set up the initial values in the array, is shown in Program 8.9.

Program 8.9   Finding the Minimum Value in an Array

// Function to find the minimum value in an array

#include <stdio.h>

int minimum (int values[10])

{

int minValue, i;

minValue = values[0];

for ( i = 1; i < 10; ++i )

if ( values[i] < minValue )

minValue = values[i];

return minValue;

}

int main (void)

{

int scores[10], i, minScore;

int minimum (int values[10]);

printf (“Enter 10 scores\n”);

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

scanf (“%i”, &scores[i]);

minScore = minimum (scores);

printf (“\nMinimum score is %i\n”, minScore);

return 0;

}

Program 8.9   Output

Enter 10 scores

69

97

65

87

69

86

78

67

92

90

Minimum score is 65

The first thing that catches your eye inside main is the prototype declaration for the minimum function. This tells the compiler that minimum returns an int and takes an array of 10 integers. Remember, it’s not necessary to make this declaration here because the minimum function is defined before it’s called from inside main. However, play it safe throughout the rest of this text and declare all functions that are used.

After the array scores is defined, the user is prompted to enter 10 values. The scanf call places each number as it is keyed in into scores[i], where i ranges from 0 through 9. After all the values have been entered, the minimum function is called with the array scores as an argument.

The formal parameter name values is used to reference the elements of the array inside the function. It is declared to be an array of 10 integer values. The local variable minValue is used to store the minimum value in the array and is initially set to values[0], the first value in the array. The for loop sequences through the remaining elements of the array, comparing each element in turn against the value of minValue. If the value of values[i] is less than minValue,a new minimum in the array has been found. In such a case, the value of minValue is reassigned to this new minimum value and the scan through the array continues.

When the for loop has completed execution, minValue is returned to the calling routine, where it is assigned to the variable minScore and is then displayed.

With your general-purpose minimum function in hand, you can use it to find the minimum of any array containing 10 integers. If you had five different arrays containing 10 integers each, you could simply call the minimum function five separate times to find the minimum value of each array. In addition, you can just as easily define other func- tions to perform tasks, such as finding the maximum value, the median value, the mean (average) value, and so on.

By defining small, independent functions that perform well-defined tasks, you can build upon these functions to accomplish more sophisticated  tasks and also make use of them for other related programming applications. For example, you could define a func- tion statistics, which takes an array as an argument and perhaps, in turn, calls a mean function, a standardDeviation function, and so on, to accumulate statistics about an array. This type of program methodology is the key to the development of programs that are easy to write, understand, modify, and maintain.

Of course, your general-purpose minimum function is not so general purpose in the sense that it only works on an array of precisely 10 elements. But this problem is rela- tively easy to rectify.You can extend the versatility of this function by having it take the number of elements in the array as an argument. In the function declaration, you can then omit the specification of the number of elements contained in the formal parame- ter array. The C compiler actually ignores this part of the declaration anyway; all the compiler is concerned with is the fact that an array is expected as an argument to the function and not how many elements are in it.

Program 8.10 is a revised version of Program 8.9 in which the minimum function finds the minimum value in an integer array of arbitrary length.

Program 8.10   Revising  the Function  to Find the Minimum Value in an Array

// Function to find the minimum value in an array

#include <stdio.h>

int minimum (int values[], int numberOfElements)

{

int minValue, i;

minValue = values[0];

for ( i = 1; i < numberOfElements;  ++i )

if ( values[i] < minValue )

minValue = values[i];

return minValue;

}

int main (void)

{

int array1[5] = { 157, -28, -37, 26, 10 };

int array2[7] = { 12, 45, 1, 10, 5, 3, 22 };

int minimum (int values[], int numberOfElements);

printf (“array1 minimum: %i\n”, minimum (array1, 5));

printf (“array2 minimum: %i\n”, minimum (array2, 7));

return 0;

}

Program 8.10   Output

array1 minimum: -37

array2 minimum: 1

This time, the function minimum is defined to take two arguments: first, the array whose minimum you want to find and second, the number of elements in the array. The open and close brackets that immediately follow values in the function header serve to inform the C compiler that values is an array of integers. As stated previously, the com- piler really doesn’t need to know how large it is.

The formal parameter numberOfElements replaces the constant 10 as the upper limit inside the for statement. So the for statement sequences through the array from values[1] through the last element of the array, which is values[numberOfElements – 1].

In the main routine, two arrays called array1 and array2 are defined to contain five and seven elements, respectively.

Inside the first printf call, a call is made to the minimum function with the arguments array1 and 5. This second argument specifies the number of elements contained in array1. The minimum function finds the minimum value in the array and the returned result of –37 is then displayed at the terminal. The second time the minimum function is called, array2 is passed, together with the number of elements in that array. The result of 1 as returned by the function is then passed to the printf function to be displayed.

1. Assignment  Operators

Study Program 8.11 and try to guess the output before looking at the actual program results.

Program 8.11   Changing Array Elements in Functions

#include <stdio.h>

void multiplyBy2 (float array[], int n)

{

int i;

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

array[i] *= 2;

}

int main (void)

{

float floatVals[4] = { 1.2f, -3.7f, 6.2f, 8.55f };

int   i;

void  multiplyBy2 (float array[], int n);

multiplyBy2 (floatVals, 4);

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

printf (“%.2f “, floatVals[i]);

printf (“\n”);

return 0;

}

Program 8.11   Output

2.40  -7.40  12.40  17.10

When you were examining Program 8.11, your attention surely must have been drawn to the following statement:

array[i] *= 2;

The effect of the “times equals” operator (*=) is to multiply the expression on the left side of the operator by the expression on the right side of the operator and to store the result back into the variable on the left side of the operator. So, the previous expression is equivalent to the following statement:

array[i] = array[i] * 2;

Getting back to the main point to be made about the preceding program, you might have realized by now that the function multiplyBy2 actually changes values inside the floatVals array. Isn’t this a contradiction to what you learned before about a function not being able to change the value of its arguments? Not really.

This program example points out one major distinction that must always be kept in mind when dealing with array arguments: If a function changes the value of an array element, that change is made to the original array that was passed to the function. This change remains in effect even after the function has completed execution and has returned to the calling routine.

The reason an array behaves differently from a simple variable or an array element— whose value cannot be changed by a function—is worthy of explanation. As mentioned previously, when a function is called, the values that are passed as arguments to the func- tion are copied into the corresponding formal parameters. This statement is still valid. However, when dealing with arrays, the entire contents of the array are not copied into the formal parameter array. Instead, the function gets passed information describing  where in the computer’s memory the array is located. Any changes made to the formal parame- ter array by the function are actually made to the original array passed to the function, and not to a copy of the array. Therefore, when the function returns, these changes still remain in effect.

Remember, the discussion about changing array values in a function applies only to entire arrays that are passed as arguments,  and not to individual elements, whose values are copied into the corresponding formal parameters and, therefore, cannot be perma- nently changed by the function. Chapter 11 discusses this concept in greater detail.

2. Sorting Arrays

To further illustrate the idea that a function can change values in an array passed as an argument, you will develop a function to sort (rank) an array of integers. The process of sorting has always received much attention by computer scientists, probably because sort- ing is an operation that is so commonly performed. Many sophisticated algorithms have been developed to sort a set of information in the least amount of time, using as little of the computer’s memory as possible. Because the purpose of this book is not to teach such sophisticated algorithms, you develop a sort function that uses a fairly straightfor- ward algorithm to sort an array into ascending order. Sorting an array into ascending order means rearranging the values in the array so that the elements progressively increase in value from the smallest to the largest. By the end of such a sort, the minimum value is contained in the first location of the array, whereas the maximum value is found in the last location of the array, with values that progressively increase in between.

If you want to sort an array of n elements into ascending order, you can do so by per- forming a successive comparison of each of the elements of the array.You can begin by comparing the first element in the array against the second. If the first element is greater in value than the second, you simply “swap” the two values in the array; that is, exchange the values contained in these two locations.

Next, compare the first element in the array (which you now know is less than the second) against the third element in the array. Once again, if the first value is greater than the third, you exchange these two values. Otherwise, you leave them alone. Now, you have the smallest of the first three elements contained in the first location of the array.

If you repeat the previous process for the remaining elements in the array—compar- ing the first element against each successive element and exchanging their values if the former is larger than the latter—the smallest value of the entire array is contained in the first location of the array by the end of the process.

If you now did the same thing with the second element of the array, that is, compare it against the third element, then against the fourth, and so on; and if you exchange any values that are out of order, you end up with the next smallest value contained in the second location of the array when the process is complete.

It should now be clear how you can go about sorting the array by performing these successive comparisons and exchanges  as needed. The process stops after you have com- pared the next-to-last element of the array against the last and have interchanged their values if required. At that point, the entire array has been sorted into ascending order.

The following algorithm gives a more concise description of the preceding sorting process. This algorithm assumes that you are sorting an array a of n elements.

Simple  Exchange Sort Algorithm

Step 1:        Set i to 0.

Step 2:        Set j to i + 1.

Step 3:        If a[i] > a[j], exchange their values.

Step 4:        Set j to j + 1. If j < n, go to step 3.

Step 5:        Set i to i + 1. If i < n – 1, go to step 2.

Step 6:         a is now sorted in ascending order.

Program 8.12 implements the preceding algorithm in a function called sort, which takes two arguments: the array to be sorted and the number of elements in the array.

Program 8.12   Sorting an Array of Integers into Ascending  Order

// Program to sort an array of integers into ascending order

#include <stdio.h>

void sort (int a[], int n)

{

int i, j, temp;

for ( i = 0; i < n – 1; ++i )

for ( j = i + 1; j < n; ++j )

if ( a[i] > a[j] ) {

temp = a[i];

a[i] = a[j];

a[j] = temp;

}

}

int main (void)

{

int i;

int array[16] = { 34, -5, 6, 0, 12, 100, 56, 22,

44, -3, -9, 12, 17, 22, 6, 11 };

void sort (int a[], int n);

printf (“The array before the sort:\n”);

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

printf (“%i “, array[i]);

sort (array, 16);

printf (“\n\nThe array after the sort:\n”);

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

printf (“%i “, array[i]);

printf (“\n”);

return 0;

}

 

Program 8.12   Output

The array before the sort:

34 -5 6 0 12 100 56 22 44 -3 -9 12 17 22 6 11

The array after the sort:

-9 -5 -3 0 6 6 11 12 12 17 22 22 34 44 56 100

The sort function implements the algorithm as a set of nested for loops. The outer- most loop sequences through the array from the first element through the next-to-last element (a[n-2]). For each such element, a second for loop is entered, which starts from the element after the one currently selected by the outer loop and ranges through the last element of the array.

If the elements are out of order (that is, if a[i] is greater than a[j]), the elements are switched. The variable temp is used as a temporary storage place while the switch is being made.

When both for loops are finished, the array has been sorted into ascending order. Execution of the function is then complete.

In the main routine, array is defined and initialized to 16 integer values. The pro- gram then displays the values of the array at the terminal and proceeds to call the sort function, passing as arguments array and 16, the number of elements in array. After the function returns, the program once again displays the values contained in array. As you can see from the output, the function successfully sorted the array into ascending order.

The sort function shown in Program 8.12 is fairly simple. The price that must be paid for such a simplistic approach is one of execution time. If you have to sort an extremely large array of values (arrays containing thousands of elements, for example), the sort rou- tine as you have implemented it here could take a considerable amount of execution time. If this happened, you would have to resort to one of the more sophisticated algo- rithms. The Art of Computer  Programming,Volume 3, Sorting and Searching (Donald E.Knuth, Addison-Wesley) is a classic reference source for such algorithms.2

3. Multidimensional  Arrays

A multidimensional array element can be passed to a function just as any ordinary vari- able or single-dimensional array element can. So the statement

squareRoot (matrix[i][j]);

calls the squareRoot function, passing the value contained in matrix[i][j] as the argu- ment.

An entire multidimensional array can be passed to a function in the same way that a single-dimensional array can:You simply list the name of the array. For example, if the matrix measured_values is declared to be a two-dimensional array of integers, the C statement

scalarMultiply (measured_values, constant);

can be used to invoke a function that multiplies each element in the matrix by the value of constant. This implies, of course, that the function itself can change the values con- tained inside the measured_values array. The discussion of this topic for single- dimensional arrays also applies here: An assignment made to any element of the formal parameter array inside the function makes a permanent change to the array that was passed to the function.

When declaring a single-dimensional array as a formal parameter inside a function, you learned that the actual dimension of the array is not needed; simply use a pair of empty brackets to inform the C compiler that the parameter is, in fact, an array. This does not totally apply in the case of multidimensional arrays. For a two-dimensional array, the number of rows in the array can be omitted, but the declaration must contain the number of columns in the array. So the declarations

int array_values[100][50]

and

int array_values[][50]

are both valid declarations for a formal parameter array called array_values containing

100 rows by 50 columns; but the declarations

int array_values[100][]

and

int array_values[][]

are not because the number of columns in the array must be specified.

In Program 8.13, you define a function scalarMultiply, which multiplies a two- dimensional integer array by a scalar integer value. Assume for purposes of this example that the array is dimensioned 3 x 5. The main routine calls the scalarMultiply routine twice. After each call, the array is passed to the displayMatrix routine to display the contents of the array. Pay careful attention to the nested for loops that are used in both scalarMultiply and displayMatrix to sequence through each element of the two- dimensional array.

Program 8.13   Using Multidimensional  Arrays and Functions

#include <stdio.h>

int main (void)

{

void scalarMultiply (int matrix[3][5], int scalar);

void displayMatrix (int matrix[3][5]);

int sampleMatrix[3][5] =

{

{ 7, 16, 55, 13, 12 },

{ 12, 10, 52, 0, 7 },

{ -2, 1, 2, 4, 9 }

};

printf (“Original matrix:\n”);

displayMatrix (sampleMatrix);

scalarMultiply (sampleMatrix, 2);

printf (“\nMultiplied by 2:\n”);

displayMatrix (sampleMatrix);

scalarMultiply (sampleMatrix, -1);

printf (“\nThen multiplied by -1:\n”);

displayMatrix (sampleMatrix);

return 0;

}

// Function to multiply a 3 x 5 array by a scalar

void scalarMultiply (int matrix[3][5], int scalar)

{

int row, column;

for ( row = 0; row < 3; ++row )

for ( column = 0; column < 5; ++column )

matrix

[column]  *= scalar;

}

void displayMatrix (int matrix[3][5])

{

int  row, column;

for ( row = 0; row < 3; ++row) {

for ( column = 0; column < 5; ++column )

printf (“%5i”, matrix

[column]);

printf (“\n”);

}

}

Program 8.13   Output

The main routine defines the matrix sampleValues and then calls the displayMatrix function to display its initial values at the terminal. Inside the displayMatrix routine, notice the nested for statements. The first or outermost for statement sequences through each row in the matrix, so the value of the variable row varies from 0 through 2. For each value of row, the innermost for statement is ex This for statement sequences through each column of the particular row, so the value of the variable column ranges from 0 through 4.

The printf statement displays the value contained in the specified row and column using the format characters %5i to ensure that the elements line up in the display. After the innermost for loop has finished execution—meaning that an entire row of the matrix has been displayed—a newline character is displayed so that the next row of the matrix is displayed on the next line of the terminal.

The first call to the scalarMultiply function specifies that the sampleMatrix array is to be multiplied by 2. Inside the function, a simple set of nested for loops is set up to sequence through each element in the array. The element contained in matrix

[column] is multiplied by the value of scalar in accordance with the use of the assignment operator *=. After the function returns to the main routine, the displayMatrix function is once again called to display the contents of the sampleMatrix array. The program’s output verifies that each element in the array has, in fact, been multiplied by 2.

The scalarMultiply function is called a second time to multiply the now modified elements of the sampleMatrix array by –1. The modified array is then displayed by a final call to the displayMatrix function, and program execution is then complete.

4. Multidimensional Variable-Length Arrays and Functions

You can take advantage of the variable-length array feature in the C language and write functions that can take multidimensional  arrays of varying sizes. For example, Program 8.13 can been rewritten so that the scalarMultiply and displayMatrix functions can accept matrices containing any number of rows and columns, which can be passed as arguments to the functions. See Program 8.13A.

Program 8.13A   Multidimensional Variable-Length Arrays

#include <stdio.h>

int main (void)

{

void scalarMultiply (int nRows, int nCols,

int matrix[nRows][nCols], int scalar);

void displayMatrix (int nRows, int nCols, int matrix[nRows][nCols]);

int  sampleMatrix[3][5] =

{

{ 7, 16, 55, 13, 12 },

{ 12, 10, 52, 0, 7 },

{ -2, 1, 2, 4, 9 }

};

printf (“Original matrix:\n”);

displayMatrix (3, 5, sampleMatrix);

scalarMultiply (3, 5, sampleMatrix, 2);

printf (“\nMultiplied by 2:\n”);

displayMatrix (3, 5, sampleMatrix);

scalarMultiply (3, 5, sampleMatrix, -1);

printf (“\nThen multiplied by -1:\n”);

displayMatrix (3, 5, sampleMatrix);

return 0;

}

// Function to multiply a matrix by a scalar

void scalarMultiply (int nRows, int nCols,

   int matrix[nRows][nCols], int scalar)

{

int row, column;

for ( row = 0; row < nRows; ++row )

for ( column = 0; column < nCols; ++column )

matrix

[column]  *= scalar;

}

void displayMatrix (int nRows, int nCols, int matrix[nRows][nCols])

{

int  row, column;

for ( row = 0; row < nRows; ++row) {

for ( column = 0; column < nCols; ++column )

printf (“%5i”, matrix

[column]);

printf (“\n”);

}

}

Program 8.13A   Output

The function declaration for scalarMultiply looks like this:

void scalarMultiply (int nRows, int nCols, int matrix[nRows][nCols], int scalar)

The rows and columns in the matrix, nRows, and nCols, must be listed as arguments before the matrix itself so that the compiler knows about these parameters before it encounters the declaration of matrix in the argument list. If you try it this way instead:

void scalarMultiply (int matrix[nRows][nCols], int nRows, int nCols, int scalar)

you get an error from the compiler because it doesn’t know about nRows and nCols when it sees them listed in the declaration of matrix.

As you can see, the output shown in Program 8.13A matches that shown in Program 8.13. Now, you have two functions (scalarMultiply and displayMatrix) that you can use with matrices of any size. This is one of the advantages of using variable-length arrays.3

Source: Kochan Stephen G. (2004), Programming in C: A Complete Introduction to the C Programming Language, Sams; Subsequent edition.

Leave a Reply

Your email address will not be published. Required fields are marked *