8.6 Using const with Pointers

Recall that const enables you to inform the compiler that the value of a particular variable should not be modified. Many possibilities exist for using (or not using) const with function parameters, so how do you choose the most appropriate? Let the principle of least privilege be your guide. Always give a function enough access to the data in its parameters to accomplish its specified task, but no more. This section discusses how to combine const with pointer declarations to enforce the principle of least privilege.

Chapter 6 explained that when an argument is passed by value, a copy of the argument is passed to the function. If the copy is modified in the called function, the original value in the caller does not change. In some instances, even the copy of the argument’s value should not be altered in the called function.

Consider a function that takes a pointer to the initial element of a built-in array and the array’s size as arguments and subsequently displays the built-in array’s elements. Such a function should loop through the elements and output each individually. The built-in array’s size is used in the function’s body to determine the highest subscript so the loop can terminate when the displaying completes. The size does not need to change in the function body, so it should be declared const to ensure that it will not change. Because the built-in array is only being displayed, it, too, should be declared const. This is especially important because built-in arrays are always passed by reference and could easily be changed in the called function. An attempt to modify a const value is a compilation error.

Software Engineering Observation 8.3

If a value does not (or should not) change in the body of a function to which it’s passed, the parameter should be declared const.

 

Error-Prevention Tip 8.4

Before using a function, check its function prototype to determine the parameters that it can and cannot modify.

There are four ways to pass a pointer to a function:

  • a nonconstant pointer to nonconstant data,

  • a nonconstant pointer to constant data (Fig. 8.10),

  • a constant pointer to nonconstant data (Fig. 8.11) and

  • a constant pointer to constant data (Fig. 8.12).

Each combination provides a different level of access privilege.

8.6.1 Nonconstant Pointer to Nonconstant Data

The highest access is granted by a nonconstant pointer to nonconstant data:

  • the data can be modified through the dereferenced pointer, and

  • the pointer can be modified to point to other data.

Such a pointer’s declaration (e.g., int* countPtr) does not include const.

8.6.2 Nonconstant Pointer to Constant Data

A nonconstant pointer to constant data is

  • a pointer that can be modified to point to any data of the appropriate type, but

  • the data to which it points cannot be modified through that pointer.

Such a pointer might be used to receive a built-in array argument to a function that should be allowed to read the elements, but not modify them. Any attempt to modify the data in the function results in a compilation error. The declaration for such a pointer places const to the left of the pointer’s type, as in1


const int* countPtr;

The declaration is read from right to left as “countPtr is a pointer to an integer constant” or more precisely, “countPtr is a nonconstant pointer to an integer constant.

Figure 8.10 demonstrates GNU C++’s compilation error message produced when attempting to compile a function that receives a nonconstant pointer to constant data, then tries to use that pointer to modify the data.

Fig. 8.10 Attempting to modify data through a nonconstant pointer to const data.

Alternate View

 1   // Fig. 8.10: fig08_10.cpp
 2   // Attempting to modify data through a
 3   // nonconstant pointer to constant data.
 4
 5   void f(const int*); // prototype
 6
 7   int main() {
 8      int y{0};
 9
10      f(&y); // f will attempt an illegal modification
11   }
12
13   // constant variable cannot be modified through xPtr
14   void f(const int* xPtr) {
15      *xPtr = 100; // error: cannot modify a const object
16   }

GNU C++ compiler error message:


fig08_10.cpp: In function ‘void f(const int*)’:
fig08_10.cpp:17:12: error: assignment of read-only location ‘* xPtr’

When a function is called with a built-in array as an argument, its contents are effectively passed by reference because the built-in array’s name is implicitly convertible to the address of the built-in array’s first element. However, by default, objects such as arrays and vectors are passed by value—a copy of the entire object is passed. This requires the execution-time overhead of making a copy of each data item in the object and storing it on the function-call stack. When a pointer to an object is passed, only a copy of the address of the object must be made—the object itself is not copied.

Performance Tip 8.1

If they do not need to be modified by the called function, pass large objects using pointers to constant data or references to constant data, to obtain the performance benefits of pass-by-reference and avoid the copy overhead of pass-by-value.

 

Software Engineering Observation 8.4

Passing large objects using pointers to constant data, or references to constant data, offers the security of pass-by-value.

 

Software Engineering Observation 8.5

Use pass-by-value to pass fundamental-type arguments (e.g., ints, doubles, etc.) to a function unless the caller explicitly requires that the called function be able to directly modify the value in the caller. This is another example of the principle of least privilege.

8.6.3 Constant Pointer to Nonconstant Data

A constant pointer to nonconstant data is a pointer that

  • always points to the same memory location, and

  • the data at that location can be modified through the pointer.

Pointers that are declared const must be initialized when they’re declared, but if the pointer is a function parameter, it’s initialized with the pointer that’s passed to the function.

Figure 8.11 attempts to modify a constant pointer. Line 9 declares pointer ptr to be of type int* const. The declaration is read from right to left as “ptr is a constant pointer to a nonconstant integer.” The pointer is initialized with the address of integer variable x. Line 12 attempts to assign the address of y to ptr, but the compiler generates an error message. No error occurs when line 11 assigns the value 7 to *ptr—the nonconstant value to which ptr points can be modified using the dereferenced ptr, even though ptr itself has been declared const.

Fig. 8.11 Attempting to modify a constant pointer to nonconstant data.

Alternate View

 1   // Fig. 8.11: fig08_11.cpp
 2   // Attempting to modify a constant pointer to nonconstant data.
 3
 4   int main() {
 5      int x, y;
 6
 7      // ptr is a constant pointer to an integer that can be modified   
 8      // through ptr, but ptr always points to the same memory location.
 9      int* const ptr{&x}; // const pointer must be initialized          
10
11      *ptr = 7; // allowed: *ptr is not const
12      ptr = &y; // error: ptr is const; cannot assign to it a new address
13   }

Microsoft Visual C++ compiler error message:


'ptr': you cannot assign to a variable that is const

8.6.4 Constant Pointer to Constant Data

The minimum access privilege is granted by a constant pointer to constant data:

  • such a pointer always points to the same memory location, and

  • the data at that location cannot be modified via the pointer.

This is how a built-in array should be passed to a function that only reads from the array, using array subscript notation, and does not modify it. The program of Fig. 8.12 declares pointer variable ptr to be of type const int* const (line 12). This declaration is read from right to left as “ptr is a constant pointer to an integer constant.” The figure shows the Xcode LLVM compiler’s error messages that are generated when an attempt is made to modify the data to which ptr points (line 16) and when an attempt is made to modify the address stored in the pointer variable (line 17)—these show up on the lines of code with the errors in the Xcode text editor. In line 14, no errors occur when the program attempts to dereference ptr, or when the program attempts to output the value to which ptr points, because neither the pointer nor the data it points to is being modified in this statement.

Fig. 8.12 Attempting to modify a constant pointer to constant data.

Alternate View

 1   // Fig. 8.12: fig08_12.cpp
 2   // Attempting to modify a constant pointer to constant data.
 3   #include <iostream>
 4   using namespace std;
 5
 6   int main() {
 7      int x{5}, y;
 8
 9      // ptr is a constant pointer to a constant integer.
10      // ptr always points to the same location; the integer
11      // at that location cannot be modified.
12      const int* const ptr{&x};
13
14      cout << *ptr << endl;
15
16      *ptr = 7; // error: *ptr is const; cannot assign new value 
17      ptr = &y; // error: ptr is const; cannot assign new address
18   }

Xcode LLVM compiler error message:


Read-only variable is not assignable
Read-only variable is not assignable
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset