Pointers/Memory Allocation   «Prev  Next»
Lesson 3 Creating const pointer arguments to functions
Objective Examine use of keyword const to create pointer arguments

Creating const pointer Argument Declarations to Functions

In C++, the keyword `const` is used to indicate that a function parameter, particularly a pointer, should not be used to modify the data it points to. This ensures that the function can read the data pointed to by the pointer, but cannot change it. Using `const` with pointers in function arguments can be done in two primary ways:
  1. Pointer to Constant Data: This declares that the data being pointed to will not be modified by the function. The syntax is `const Type* pointerName`, where `Type` is the data type of the variable the pointer points to.
  2. Constant Pointer: This declares that the pointer itself cannot be changed to point to a different memory location, although the data it points to can be modified. The syntax is `Type* const pointerName`.
    Here's an example demonstrating both usages in the context of a function:
    #include <iostream>
    
    // Function with a pointer to constant data
    void printArray(const int* arr, int size) {
        for (int i = 0; i < size; ++i) {
            std::cout << arr[i] << " ";
            // arr[i] = i; // This line would cause a compilation error
        }
        std::cout << "\n";
    }
    
    // Function with a constant pointer
    void incrementAll(int* const arr, int size) {
        for (int i = 0; i < size; ++i) {
            arr[i]++;
        }
        // arr = nullptr; // This line would cause a compilation error
    }
    
    int main() {
        int numbers[] = {1, 2, 3, 4, 5};
        int size = sizeof(numbers) / sizeof(numbers[0]);
    
        printArray(numbers, size); // Prints the array elements
        incrementAll(numbers, size); // Increments each element by 1
        printArray(numbers, size); // Prints the modified array elements
    
        return 0;
    }
    

In `printArray`, `const int* arr` means the function can use `arr` to read the array elements but cannot modify them. In `incrementAll`, `int* const arr` means the function can modify the elements pointed by `arr` but cannot change the `arr` pointer to point elsewhere. This usage of `const` improves code readability and safety, indicating the intended use of the pointers to other developers and to the compiler.

Big C++

pointer whose pointed at value is constant

const type * identifier

declares the identifier as a pointer whose pointed at value is constant. This construct is used when pointer arguments to functions will not have their contents modified.
So
void fcn(const int* p){
  // within here it is illegal
  // to have  *p = expression;
}

This provides "const safety" allowing the compiler to detect an error. It also allows the compiler to optimize how these arguments are passed.
  • Non-const pointers
    Let us contrast this with a non-const pointer argument:
    void fcn(int* p){
     // within here it is legal
     // to have  *p = expression;
    }
    

    Now attempting to pass a const pointer value will lead to a syntax error.
    const int size = 100;  //size may not be modified
    fcn(&size);  //illegal because the address
      // of a const variable is being passed to an
      // argument that allows for modification
      // of what is pointed at.
    

Declaring a pointer constant

We can take this one step further. The form
const type* const identifier

declares the identifier as a
Pointer Constant Example
The statement
const int* const cp;

declares cp to be a constant pointer whose pointed-at value is also constant.
void fcn(const int* const p){
   // within here it is illegal
   // to have  *p = expression;
   // also illegal is p = expression; 
}


Constant Pointers and Pointers to Constants

const char* pstars[] {
"Fatty Arbuckle", "Clara Bow",
"Lassie", "Slim Pickens",
"Boris Karloff", "Mae West",
"Oliver Hardy", "Greta Garbo"
};

Here you are specifying that the objects pointed to by elements of the array are constant. The compiler inhibits any direct attempt to change these, so an assignment statement such as this would be flagged as an error by the compiler, thus preventing a nasty problem at runtime:
*pstars[0] = 'X'; // Will not compile...

However, you could still legally write the next statement, which would copy the address stored in the element on the right of the assignment operator to the element on the left:
pstars[5] = pstars[6]; // OK

Those lucky individuals due to be awarded Ms. West would now get Mr. Hardy, because both pointers now point to the same name. Of course, this has not changed the object pointed to by the sixth array element, it has only changed the address stored in it, so the const specification has not been breached. You really ought to be able to inhibit this kind of change as well, because some people may reckon that good old Ollie may not have quite the same sex appeal as Mae, and of course you can.
Look at this statement:
const char* const pstars[] {
 "Fatty Arbuckle", "Clara Bow",
 "Lassie", "Slim Pickens",
 "Boris Karloff", "Mae West",
 "Oliver Hardy", "Greta Garbo"
};


Examples of const variable declaration

Here are some other examples of const variable declaration:
                
const double pi = 3.14159;
double  x = 1.5;
const double *d_p1 = &pi; //legal
double *d_p2 = &x; //legal
const double *d_p2 = &3.1; //illegal:3.1 is not an lvalue
pi = 3.141596; //illegal: pi cannot be modified
d_p1 = &x; //legal: safe conversion
d_p2 = &pi; //illegal: would allow a const to be modified through a non-const pointer

  • Hexadecimal Constants
    You can write integer values in hexadecimal form to base 16. The digits in a hexadecimal number are the equivalent of decimal values 0 to 15, and they are represented by 0 through 9 and A though F (or a through f). Because there needs to be a way to distinguish between 9910 and 9916, hexadecimal numbers are written with the prefix 0x or 0X. You would therefore write 9916 in your program as 0x99 or as 0X99. Hexadecimal constants can also have a suffix.
    Here are some examples of hexadecimal constants:
    0xFFFF 0xdead 0xfade 0xFade 0x123456EE 0xafL 0xFABABULL
    

    Hexadecimal constants are used to specify bit Patterns: The last example is of type unsigned long long, and the second to last example is of type long. Hexadecimal constants are most often used to specify bit patterns, because each hexadecimal digit corresponds to four bits. Two hexadecimal digits specify a byte. The bitwise operators are usually used with hexadecimal constants that define masks. If you are unfamiliar with hexadecimal numbers, just remember they are a number system which uses 16 as a base.
  • Octal Constants: An octal value is a number to base 8. Each octal digit has a value from 0 to 7, which corresponds to three bits in binary. Octal values originate from the days long ago when computer memory was in terms of 36-bit words, so a word was a multiple of three bits. Thus, a 36-bit binary word could be written as 12 octal digits. Octal constants[1] are rarely used these days, but you need to be aware of them so you do not specify an octal constant by mistake. An integer constant that starts with a zero, such as 014, will be interpreted by your compiler as an octal number. Thus, 014 is the octal equivalent of the decimal value 12. If you meant it to be the decimal value 14, it is obviously wrong, so do not put a leading zero in your integers unless you really want to specify an octal value. There is rarely a need to use octal values.

[1]: The C Standard defines octal constants as a 0 followed by octal digits (0 1 2 3 4 5 6 7).

SEMrush Software