7.5. Passing Arrays to Functions

To pass an array argument to a function, specify the name of the array without any brackets. For example, if array hourlyTemperatures has been declared as

int hourlyTemperatures[ 24 ];

the function call

modifyArray( hourlyTemperatures, 24 );

passes array hourlyTemperatures and its size to function modifyArray. When passing an array to a function, the array size is normally passed as well, so the function can process the specific number of elements in the array. Otherwise, we would need to build this knowledge into the called function itself or, worse yet, place the array size in a global variable. In Section 7.11, when we present C++ Standard Library class template vector to represent a more robust type of array, you'll see that the size of a vector is built in—every vector object "knows" its own size, which can be obtained by invoking the vector object's size member function. Thus, when we pass a vector object into a function, we will not have to pass the size of the vector as an argument.

C++ passes arrays to functions by reference—the called functions can modify the element values in the callers' original arrays. The value of the name of the array is the address in the computer's memory of the first element of the array. Because the starting address of the array is passed, the called function knows precisely where the array is stored in memory. Therefore, when the called function modifies array elements in its function body, it is modifying the actual elements of the array in their original memory locations.

Performance Tip 7.2

Passing arrays by reference makes sense for performance reasons. If arrays were passed by value, a copy of each element would be passed. For large, frequently passed arrays, this would be time consuming and would require considerable storage for the copies of the array elements.


Software Engineering Observation 7.3

It is possible to pass an array by value (by using a simple trick we explain in Chapter 19)—however, this is rarely done.


Although entire arrays are passed by reference, individual array elements are passed by value exactly as simple variables are. Such simple single pieces of data are called scalars or scalar quantities. To pass an element of an array to a function, use the subscripted name of the array element as an argument in the function call. In Chapter 6, we showed how to pass scalars (i.e., individual variables and array elements) by reference with references. In Chapter 8, we show how to pass scalars by reference with pointers.

For a function to receive an array through a function call, the function's parameter list must specify that the function expects to receive an array. For example, the function header for function modifyArray might be written as

void modifyArray( int b[], int arraySize )

indicating that modifyArray expects to receive the address of an array of integers in parameter b and the number of array elements in parameter arraySize. The array's size is not required in the array brackets. If it is included, the compiler ignores it; thus, arrays of any size can be passed to the function. C++ passes arrays to functions by reference—when the called function uses the array name b, it refers to the actual array in the caller (i.e., array hourlyTemperatures discussed at the beginning of this section).

Note the strange appearance of the function prototype for modifyArray

void modifyArray( int [], int );

This prototype could have been written

void modifyArray( int anyArrayName[], int anyVariableName );

but, as we saw in Chapter 3, C++ compilers ignore variable names in prototypes. Remember, the prototype tells the compiler the number of arguments and the type of each argument (in the order in which the arguments are expected to appear).

The program in Fig. 7.14 demonstrates the difference between passing an entire array and passing an array element. Lines 22–23 print the five original elements of integer array a. Line 28 passes a and its size to function modifyArray (lines 45–50), which multiplies each of a's elements by 2 (through parameter b). Then, lines 32–33 print array a again in main. As the output shows, the elements of a are indeed modified by modifyArray. Next, line 36 prints the value of scalar a[3], then line 38 passes element a[ 3 ] to function modifyElement (lines 54–58), which multiplies its parameter by 2 and prints the new value. Note that when line 39 again prints a[3] in main, the value has not been modified, because individual array elements are passed by value.

Fig. 7.14. Passing arrays and individual array elements to functions.

 

 1  // Fig. 7.14: fig07_14.cpp
 2   // Passing arrays and individual array elements to functions.
 3   #include <iostream>
 4   using std::cout;
 5   using std::endl;
 6
 7   #include <iomanip>
 8   using std::setw;
 9
10   void modifyArray( int [], int ); // appears strange; array and size
11   void modifyElement( int ); // receive array element value          
12
13   int main()
14   {
15      const int arraySize = 5; // size of array a
16      int a[ arraySize ] = { 0, 1, 2, 3, 4 }; // initialize array a
17
18      cout << "Effects of passing entire array by reference:"
19         << "\n\nThe values of the original array are:\n";
20
21      // output original array elements
22      for ( int i = 0; i < arraySize; i++ )
23         cout << setw( 3 ) << a[ i ];
24
25      cout << endl;
26
27      // pass array a to modifyArray by reference
28      modifyArray( a, arraySize );               
29      cout << "The values of the modified array are:\n";
30
31      // output modified array elements
32      for ( int j = 0; j < arraySize; j++ )
33         cout << setw( 3 ) << a[ j ];
34
35      cout << "\n\n\nEffects of passing array element by value:"
36         << "\n\na[3] before modifyElement: " << a[ 3 ] << endl;
37
38      modifyElement( a[ 3 ] ); // pass array element a[ 3 ] by value
39      cout << "a[3] after modifyElement: " << a[ 3 ] << endl;
40
41      return 0; // indicates successful termination
42   } // end main
43
44   // in function modifyArray, "b" points to the original array "a" in memory
45   void modifyArray( int b[], int sizeOfArray )                              
46   {                                                                         
47      // multiply each array element by 2                                    
48      for ( int k = 0; k < sizeOfArray; k++ )                                
49         b[ k ] *= 2;                                                        
50   } // end function modifyArray                                             
51
52   // in function modifyElement, "e" is a local copy of                   
53   // array element a[ 3 ] passed from main                               
54   void modifyElement( int e )                                            
55   {                                                                      
56      // multiply parameter by 2                                          
57      cout << "Value of element in modifyElement: " << ( e *= 2 ) << endl;
58   } // end function modifyElement                                        

					  

Effects of passing entire array by reference:

The values of the original array are:
  0  1  2  3  4
The values of the modified array are:
  0  2  4  6  8

Effects of passing array element by value:

a[3] before modifyElement: 6
Value of element in modifyElement: 12
a[3] after modifyElement: 6


There may be situations in your programs in which a function should not be allowed to modify array elements. C++ provides the type qualifier const that can be used to prevent modification of array values in the caller by code in a called function. When a function specifies an array parameter that is preceded by the const qualifier, the elements of the array become constant in the function body, and any attempt to modify an element of the array in the function body results in a compilation error. This enables you to prevent accidental modification of array elements in the function's body.

Figure 7.15 demonstrates the const qualifier. Function tryToModifyArray (lines 21–26) is defined with parameter const int b[], which specifies that array b is constant and cannot be modified. Each of the three attempts by the function to modify array b's elements (lines 23–25) results in a compilation error. The Borland C++ compiler, for example, produces the error "Cannot modify a const object." [Note: The C++ standard defines an "object" as any "region of storage," thus including variables or array elements of fundamental data types as well as instances of classes (what we've been calling objects).] This message indicates that using a const object (e.g., b[0]) as an lvalue is an error—you cannot assign a new value to a const object by placing it on the left of an assignment operator. Note that compiler error messages vary between compilers (as shown in Fig. 7.15). The const qualifier will be discussed again in Chapter 10.

Fig. 7.15. const type qualifier applied to an array parameter.

 

 1   // Fig. 7.15: fig07_15.cpp
 2   // Demonstrating the const type qualifier.
 3   #include <iostream>
 4   using std::cout;
 5   using std::endl;
 6
 7   void tryToModifyArray( const int [] ); // function prototype
 8
 9   int main()
10   {
11      int a[] = { 10, 20, 30 };
12
13      tryToModifyArray( a );
14      cout << a[ 0 ] << ' ' << a[ 1 ] << ' ' << a[ 2 ] << '\n';
15
16      return 0; // indicates successful termination
17   } // end main
18
19   // In function tryToModifyArray, "b" cannot be used
20   // to modify the original array "a" in main.       
21   void tryToModifyArray( const int b[] )             
22   {                                                  
23      b[ 0 ] /= 2; // compilation error               
24      b[ 1 ] /= 2; // compilation error               
25      b[ 2 ] /= 2; // compilation error               
26   } // end function tryToModifyArray                 

					  

Borland C++ command-line compiler error message:
Error E2024 fig07_15.cpp 23: Cannot modify a const object
   in function tryToModifyArray(const int * const)
Error E2024 fig07_15.cpp 24: Cannot modify a const object
   in function tryToModifyArray(const int * const)
Error E2024 fig07_15.cpp 25: Cannot modify a const object
   in function tryToModifyArray(const int * const)

Microsoft Visual C++ compiler error message:
c:\cppfp_examples\ch07\fig07_15\fig07_15.cpp(23) : error C3892: 'b' : you
   cannot assign to a variable that is const
c:\cppfp_examples\ch07\fig07_15\fig07_15.cpp(24) : error C3892: 'b' : you
   cannot assign to a variable that is const
c:\cppfp_examples\ch07\fig07_15\fig07_15.cpp(25) : error C3892: 'b' : you
   cannot assign to a variable that is const

GNU C++ compiler error message:
fig07_15.cpp:23: error: assignment of read-only location
fig07_15.cpp:24: error: assignment of read-only location
fig07_15.cpp:25: error: assignment of read-only location


Common Programming Error 7.11

Forgetting that arrays in the caller are passed by reference, and hence can be modified in called functions, may result in logic errors.


Software Engineering Observation 7.4

Applying the const type qualifier to an array parameter in a function definition to prevent the original array from being modified in the function body is another example of the principle of least privilege. Functions should not be given the capability to modify an array unless it is absolutely necessary.