Previous | Table of Contents | Next |
7.2.6.1. Pointers
For every object type T, there is a corresponding pointer type pointer to T. This type is often written T*, even though referring to the type that way is potentially confusing.
Part of the confusion arises from the way pointer variables are declared. One does, indeed, write
T* p;
but some people also write
T *p;
and the latter form may be more accurate. The point is that each of these two definitions says that *p has type T, and therefore, by inference, that p has type pointer to T.
This difference in treatment is not merely pedantic, as can be seen by the definition
T *p, q;
This definition says that *p and q both have type T, so that p is a pointer but q is not!
It should be obvious by now that if p is a pointer, *p is the object to which p points. Moreover, the result of unary * is what is called an lvalue, which means that it is permitted to appear on the left side of an assignment, or, equivalently, that it has an address.
If v is an lvalue, then &v is the address of v. The lvalue requirement keeps us from writing expressions such as &3, which are meaningless because 3 does not have a meaningful address. Thus, for instance, if we say
double x; double* xp = &x;
then *xp is equivalent to x, and after executing
*xp = 3;
the value of x will be 3.0. Here, the compiler knew that *xp was a double, because of the type of xp, and therefore converted 3 to double as part of the assignment.
Pointers are independent of the objects to which they point, which implies that those objects might go away before the pointers themselves do. C++ makes no attempt to defend against such eventualities. For example:
int main() { int* p; { int x; p = &x; } *p = 42; // errordangling pointer return 0; }
There was nothing wrong with setting p to the address of x, but once x went out of scope (at the } that was paired with the last { before xs definition), any further use of p, other than to give p a new value, would be invalid. Examples such as this one show that pointers, despite their importance, are hazardous.
7.2.6.2. References
References are somewhat like pointers, but because they are much more restricted in what they can do, they are generally less hazardous. In particular, a reference is not an object, and once a reference has been created, it is not possible to make it refer to a different entity.
References are declared similarly to pointers. So, for instance, after saying
int x; int* p = &x; int& r = x;
we have established p as a pointer that points to x and r as a reference that refers to x. However, while the pointer declaration syntax has a clear analogy in the expression syntax (by saying that *p is an int), the reference declaration syntax does not. The most useful way to think about declarations of references is therefore probably to convert them mentally to declarations of pointers, by changing & to *, and parse them that way.
Because a reference cannot be rebound after it is created, one must bind a reference at the time one creates it. Therefore,
int& z; // illegal
is prohibited, because z is not bound to anything.
Returning to the declaration of r, we find that r and x are now effectively two names for the same object:
r = 3; // x is now 3
There is never any neednor even any wayto dereference a reference explicitly, the way one dereferences a pointer.
References are most useful as function parameters, where they are used to implement call by reference. For example,
void int_assign(int& dest, int source) { dest = source; }
is a function that assigns its second operand to its first. We might use that function this way:
int n; int_assign(n, 42); // n = 42
with the same effect as assigning directly to n. Using references this way is, for example, what permits expressions like cin >> n to modify the value of n. Otherwise, it would be necessary to write cin >> &n in order to grant write access to n.
Previous | Table of Contents | Next |