MFC Programmer's SourceBook : Thinking in C++
Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

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:

const int* x;

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:

int const* x;

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

*x = 2;

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:

int* u = &w;

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:

int* u = &w, v = 0;

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

char* cp = "howdy";

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.

Contents | Prev | Next


Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru