const* and *const
Suppose that we have a pointer ptr that is storing the address of a variable
vbl:Type* ptr = &vbl;
When using a pointer, two objects are involved: the pointer itself and the object pointed to. This means there are two possible layers of protection that we might want to impose with const:
- If we want to make sure that ptr cannot point to any other memory location, we can write it one of two ways:
Type* const ptr = &vbl; Type* const ptr(&vbl);
The pointer is a const but the addressed object can be changed.
- If we want to make sure that the value of vbl cannot be changed by dereferencing ptr, we can write it in two ways:
const Type* ptr = &vbl; const Type* ptr(&vbl);
In this case, the addressed object is a constant but the pointer is not.
In addition, if we want to impose both kinds of protection we can write:
const Type* const ptr = &vbl; const Type* const ptr(&vbl);
Here is a good way to remember which is which: Read each of the following definitions from right to left (starting with the defined variable).
const char * x = &p; /* x is a pointer to const char*/ char * const y = &q; /* y is a const pointer to char */ const char * const z = &r; /* z is a const pointer to a const char */
A short program that demonstrates the two kinds of protection is shown in Example 1.22.
Example 1.22. src/constptr/constptr.cpp
#include using namespace std; int main() { int m1(11), m2(13); const int* n1(&m1); int* const n2(&m2); // First snapshot cout << "n1 = " << n1 << ' ' << *n1 << ' ' << "n2 = " << n2 << ' ' << *n2 << endl; n1 = &m2; //*n1 = 15; <-- 1 m1 = 17; <-- 2 //n2 = &m1; <-- 3 *n2 = 16; <-- 4 // Second snapshot cout << "n1 = " << n1 << ' ' << *n1 << ' ' << "n2 = " << n2 << ' ' << *n2 << endl; return 0; } Output: src/constptr> g++ constptr.cpp src/constptr> ./a.out n1 = 0xbffff504 11 n2 = 0xbffff500 13 n1 = 0xbffff500 16 n2 = 0xbffff500 16 src/constptr> (1)error: assignment of read-only location (2)m2 is an ordinary int variableOK to assign (3)error: assignment of read-only variable 'n2' (4)okay to change target |
Figure 1.2 shows two snapshots of memory that may help to clarify what is happening when the program runs. Notice that the program produces a memory leak.
Figure 1.2. Two snapshots of memory showing what the program in Example 1.2 produces
An object that is read-only when accessed through one pointer may be changeable when accessed through another pointer. This fact is commonly exploited in the design of functions.
char* strcpy(char* dst, const char* src); // strcpy cannot change *src
It is okay to assign the address of a variable to a pointer to const. It is an error to assign the address of a const object to an unrestricted (i.e., non-const) pointer variable because that would allow the const object's value to be changed.
int a = 1; const int c = 2; const int* p1 = &c; // OK const int* p2 = &a; // OK int* p3 = &c; // error *p3 = 5; // error
It is good programming practice to use const to protect pointer and reference parameters that do not need to be altered by the action of a function. Read-only reference parameters provide the power and efficiency of pass-by-reference with the safety of pass-by-value (see Section 5.6).