Passing and returning addresses
If you pass or return an address (either
a pointer or a reference), it’s possible for the client programmer to take
it and modify the original value. If you make the pointer or reference a
const, you prevent this from happening, which may save you some grief. In
fact, whenever you’re passing an address into a function, you should make
it a const if at all possible. If you don’t, you’re excluding
the possibility of using that function with anything that is a
const.
The choice of whether to return a pointer
or reference to a const depends on what you want to allow your client
programmer to do with it. Here’s an example that demonstrates the use of
const pointers as function arguments and return values:
//: C08:ConstPointer.cpp
// Constant pointer arg/return
void t(int*) {}
void u(const int* cip) {
//! *cip = 2; // Illegal -- modifies value
int i = *cip; // OK -- copies value
//! int* ip2 = cip; // Illegal: non-const
}
const char* v() {
// Returns address of static character array:
return "result of function v()";
}
const int* const w() {
static int i;
return &i;
}
int main() {
int x = 0;
int* ip = &x;
const int* cip = &x;
t(ip); // OK
//! t(cip); // Not OK
u(ip); // OK
u(cip); // Also OK
//! char* cp = v(); // Not OK
const char* ccp = v(); // OK
//! int* ip2 = w(); // Not OK
const int* const ccip = w(); // OK
const int* cip2 = w(); // OK
//! *w() = 1; // Not OK
} ///:~
The function t( ) takes an
ordinary non-const pointer as an argument, and u( ) takes a
const pointer. Inside u( ) you can see that attempting to
modify the destination of the const pointer is illegal, but you can of
course copy the information out into a non-const variable. The compiler
also prevents you from creating a non-const pointer using the address
stored inside a const pointer.
The functions v( ) and
w( ) test return value
semantics. v( )
returns a const char* that is created from a character array
literal. This statement actually produces the address of the character array
literal, after the compiler creates it and stores it in the static storage area.
As mentioned earlier, this character array is technically a constant, which is
properly expressed by the return value of v( ).
The return value of w( )
requires that both the pointer and what it points to must be const. As
with v( ), the value returned by w( ) is valid after the
function returns only because it is
static. You never want to
return pointers to local stack variables because they will be invalid after the
function returns and the stack is cleaned up. (Another common pointer you might
return is the address of storage allocated on the heap, which is still valid
after the function returns.)
In main( ), the functions are
tested with various arguments. You can see that t( ) will accept a
non-const pointer argument, but if you try to pass it a pointer to a
const, there’s no promise that t( ) will leave the
pointer’s destination alone, so the compiler gives you an error message.
u( ) takes a const pointer, so it will accept both types of
arguments. Thus, a function that takes a const pointer is more general
than one that does not.
As expected, the return value of
v( ) can be assigned only to a pointer to a const. You would
also expect that the compiler refuses to assign the return value of
w( ) to a non-const pointer, and accepts a const int*
const, but it might be a bit surprising to see that it also accepts a
const int*, which is not an exact match to the return type. Once again,
because the value (which is the address contained in the pointer) is being
copied, the promise that the original variable is untouched is automatically
kept. Thus, the second const in const int* const is only
meaningful when you try to use it as an lvalue, in which case the compiler
prevents you.