Pointers
Pointers
can be made
const.
The compiler will still endeavor to prevent storage allocation and do constant
folding when dealing with
const
pointers,
but these features seem less useful in this case. More importantly, the
compiler will tell you if you attempt changes using such a pointer later in
your code, which adds a great deal of safety.
When
using
const
with pointers, you have two options:
const
can be applied to what the pointer is pointing to, or the
const
can be applied to the address stored in the pointer itself. The syntax for
these is a little confusing at first but becomes comfortable with practice.
Pointer
to const
The
trick with a pointer definition, as with any complicated definition, is to read
it starting at the identifier and working your way out. The
const
specifier binds to the thing it is “closest to.” So if you want to
prevent any changes to the element you are pointing to, you write a definition
like this:
Starting
from the identifier, we read “
x
is a pointer, which points to a
const
int.”
Here, no initialization is required because you’re saying that
x
can point to anything (that is, it is not
const),
but the thing it points to cannot be changed.
Here’s
the mildly confusing part. You might think that to make the pointer itself
unchangeable, that is, to prevent any change to the address contained inside
x,
you would simply move the
const
to the other side of the
int
like this:
It’s
not all that crazy to think that this should read “
x
is a
const
pointer to an
int.”
However, the way it
actually
reads is “
x
is an ordinary pointer to an
int
that happens to be
const.”
That is, the
const
has bound itself to the
int
again, and the effect is the same as the previous definition. The fact that
these two definitions are the same is the confusing point; to prevent this
confusion on the part of your reader, you should probably stick to the first
form.
const
pointer
To
make the pointer itself a
const,
you must place the
const
specifier to the right of the
*,
like this:
int d = 1;
int* const x = &d;
Now
it reads: “
x
is a pointer, which is
const
that points to an
int.”
Because the pointer itself is now the
const,
the compiler requires that it be given an initial value that will be unchanged
for the life of that pointer. It’s OK, however, to change what that value
points to by saying
You
can also make a
const
pointer to a
const
object using either of two legal forms:
int d = 1;
const int* const x = &d; // (1)
int const* const x2 = &d; // (2)
Now
neither the pointer nor the object can be changed.
Some
people argue that the second form is more consistent because the
const
is always placed to the right of what it modifies. You’ll have to decide
which is clearer for your particular coding style.
Formatting
This
book makes a point of only putting one pointer definition on a line, and
initializing each pointer at the point of definition whenever possible. Because
of this, the formatting style of “attaching” the ‘
*’
to the data type is possible:
as
if
int*
were a discrete type unto itself. This makes the code easier to understand, but
unfortunately that’s not actually the way things work. The ‘
*’
in fact binds to the identifier, not the type. It can be placed anywhere
between the type name and the identifier. So you can do this:
which
creates an
int*
u
,
as before, and a nonpointer
int
v
.
Because readers often find this confusing, it is best to follow the form shown
in this book.
Assignment
and type checking
C++
is very particular about type checking, and this extends to pointer
assignments. You can assign the address of a non-
const
object to a
const
pointer because you’re simply promising not to change something that is
OK to change. However, you can’t assign the address of a
const
object to a non-
const
pointer because then you’re saying you might change the object via the
pointer. Of course, you can always use a cast to force such an assignment, but
this is bad programming practice because you are then breaking the
constness
of the object, along with any safety promised by the
const.
For example:
int d = 1;
const int e = 2;
int* u = &d; // OK -- d not const
int* v = &e; // Illegal -- e const
int* w = (int*)&e; // Legal but bad practice
Although
C++ helps prevent errors it, does not protect you from yourself if you want to
break the safety mechanisms.
Character
array literals
The
place where strict
constness
is not enforced is with character array literals.
You can say
and
the compiler will accept it without complaint. This is technically an error
because a character array literal (
“howdy”
in this case) is created by the compiler as a constant character array, and the
result of the quoted character array is its starting address in memory.
So
character array literals are actually constant character arrays. Of course, the
compiler lets you get away with treating them as non-
const
because there’s so much existing C code that relies on this. However, if
you try to change the values in a character array literal, the behavior is
undefined, although it will probably work on many machines.
Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru