Section 8 - Passing Addresses (Pointers) to Methods
3A.8.1 Reference Parameters
We have seen that one way to allow a function to modify parameters in such a way, that the changes persist back up in the client (i.e, so that the arguments' values change after the function call) is to declare the formal parameters as reference parameters:
void swap( int &num1, int &num2);
Since we are exploring some of the original, more primitive C++ techniques this lesson (CStrings, for example), we will continue in that vein and describe the original technique for accomplishing this same effect. This is more than historical, however, as this method is used frequently even in contemporary C++ code.
3A.8.2 Passing Addresses to Functions
If we pass an object's address rather than the object itself, then we can have the function indirectly modify the object. We want a function to modify client float variables a, b and c. We pass the function the addresses of these three:
int main() { float a=1, b=1, c=1; modify(&a, &b, &c); // etc ... }
How does the function modify() work, and how is it defined? First of all, the prototype must be consistent with the function call. That means the formal parameters must be able to accept the arguments we send it. What type would be capable of accepting &a?
Since &a is the address of a float, the correct variable to use would be a float pointer. Here then is what the signature of modify() would have to look like:
void modify(float *x, float *y, float *z);
How does modify() use the formal parameters, x, y and z inside its definition? As with any float pointer, if we want to access the float, we must dereference the pointer using *. Here is an example:
// ------------------- prototype --------------------- void modify(float *x, float *y, float *z); // ------------------- main --------------------- int main() { float a=1, b=1, c=1; modify (&a, &b, &c); cout << a << " " << b << " " << c << endl; return 0; } // ------------------- modify --------------------- void modify(float *x, float *y, float *z) { *x += 1.5 / *x; *y += 1.75 / *y; *z += 1.9 / *z; }
with output:
2.5 2.75 2.9
As you can see, inside modify(), when we want to access the contents of the object in main(), we do it by de-referencing the pointer. Using the pointer without de-referencing, i.e. without the *, would cause the address itself to be changed, not the object pointed to by the address.
Here is a pointer (passing addresses) version of the swap() method we have already seen in our sorting program. In the original swap() we used reference parameters, i.e., swap's prototype was:
void swap( int &x, int &y);
Then, we simply passed any variables, directly into swap as arguments. In the new (or actually old) approach using address passing, we would declare swap as follows:
void swap( int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp; }
And we would invoke swap() this way:
swap (&num1, &num2);
3A.8.3 Reference Parameters vs. Passing Addresses
The notational difference between reference parameters (the first way we learned) and passing addresses (the current topic) is so small that it can be confusing. Here is a table that summarizes the difference between the two techniques.
int clientX; int clientY; | Reference Parameters | Passing Addresses |
---|---|---|
Method Invocation | swap(clientX, clientY); |
swap(&clientX, &clientY); |
Function Signature | void swap( int ¶mX, int ¶mY) |
void swap( int *paramX, int *paramY) |
Accessing formal parameters in function | temp = paramX; paramY = temp; |
temp = *paramX; *paramY = temp; |
If parameters are objects, access them inside the function like this: | temp
= paramX.fieldName; paramY.fieldName = temp; |
temp = paramX -> fieldName; paramY -> fieldName = temp; |
In brief, using reference parameters allows us to write both the arguments and the formal parameters exactly as if they were ordinary value parameters. Only the function signature changes. When passing addresses, we have to use special notation for both the arguments and the formal parameters.
That said, there are advantages to passing addresses. In fact, we will see, that when we pass arrays to methods, we are actually passing addresses.