This looks plausible. The brackets seem to indicate that arr is an array, and the fact that the brackets are empty seems to indicate that you can use the function with an array of any size. But things are not always as they seem: arr is not really an array; it’s a pointer! The good news is that you can write the rest of the function just as if arr were an array. First, let’s use an example to check that this approach works, and then let’s look into why it works.
Listing 7.5 illustrates using a pointer as if it were an array name. The program initializes the array to some values and uses the sum_arr() function to calculate the sum. Note that the sum_arr() function uses arr as if it were an array name.
Listing 7.5. arrfun1.cpp
// arrfun1.cpp -- functions with an array argument
#include
const int ArSize = 8;
int sum_arr(int arr[], int n); // prototype
int main()
{
using namespace std;
int cookies[ArSize] = {1,2,4,8,16,32,64,128};
// some systems require preceding int with static to
// enable array initialization
int sum = sum_arr(cookies, ArSize);
cout << "Total cookies eaten: " << sum << "\n";
return 0;
}
// return the sum of an integer array
int sum_arr(int arr[], int n)
{
int total = 0;
for (int i = 0; i < n; i++)
total = total + arr[i];
return total;
}
Here is the output of the program in Listing 7.5:
Total cookies eaten: 255
As you can see, the program works. Now let’s look at why it works.
How Pointers Enable Array-Processing Functions
The key to the program in Listing 7.5 is that C++, like C, in most contexts treats the name of an array as if it were a pointer. Recall from Chapter 4, “Compound Types,” that C++ interprets an array name as the address of its first element:
cookies == &cookies[0] // array name is address of first element
(There are a few exceptions to this rule. First, the array declaration uses the array name to label the storage. Second, applying sizeof to an array name yields the size of the whole array, in bytes. Third, as mentioned in Chapter 4, applying the address operator & to an array name returns the address of the whole array; for example, &cookies would be the address of a 32-byte block of memory if int is 4 bytes.)
Listing 7.5 makes the following function call:
int sum = sum_arr(cookies, ArSize);
Here cookies is the name of an array, hence by C++ rules cookies is the address of the array’s first element. The function passes an address. Because the array has type int elements, cookies must be type pointer-to-int, or int *. This suggests that the correct function header should be this:
int sum_arr(int * arr, int n) // arr = array name, n = size
Here int *arr has replaced int arr[]. It turns out that both headers are correct because in C++ the notations int *arr and int arr[] have the identical meaning when (and
Given that the variable arr actually is a pointer, the rest of the function makes sense. As you might recall from the discussion of dynamic arrays in Chapter 4, you can use the bracket array notation equally well with array names or with pointers to access elements of an array. Whether arr is a pointer or an array name, the expression arr[3] means the fourth element of the array. And it probably will do no harm at this point to remind you of the following two identities:
arr[i] == *(ar + i) // values in two notations
&arr[i] == ar + i // addresses in two notations
Remember that adding one to a pointer, including an array name, actually adds a value equal to the size, in bytes, of the type to which the pointer points. Pointer addition and array subscription are two equivalent ways of counting elements from the beginning of an array.
The Implications of Using Arrays as Arguments