What is the difference between const int*, const int * const, and int * const?

Learn what is the difference between const int*, const int * const, and int * const? with practical examples, diagrams, and best practices. Covers c++, c, pointers development techniques with visua...

Demystifying C/C++ Pointer Constants: const int*, const int* const, and int* const

Hero image for What is the difference between const int*, const int * const, and int * const?

Understand the subtle yet critical differences between various const pointer declarations in C and C++ to write safer and more robust code.

In C and C++, the const keyword is a powerful tool for enforcing immutability, which helps prevent accidental modifications and improves code safety. When const is combined with pointers, its placement becomes crucial, leading to different interpretations of what exactly is constant. This article will break down the distinctions between const int*, const int* const, and int* const, illustrating how each declaration affects the pointer itself and the data it points to.

Understanding const and Pointers

Before diving into the specific declarations, let's establish the fundamental rule for const with pointers: const applies to whatever is immediately to its left. If there's nothing to its left, it applies to whatever is immediately to its right. This rule is key to deciphering complex pointer declarations.

flowchart TD
    A[Declaration] --> B{Is 'const' to the left of '*' ?}
    B -- Yes --> C[Pointer to a constant value]
    B -- No --> D{Is 'const' to the right of '*' ?}
    D -- Yes --> E[Constant pointer to a mutable value]
    D -- No --> F[Mutable pointer to a mutable value]
    C --> G[Value cannot be changed via this pointer]
    E --> H[Pointer cannot be re-assigned]
    F --> I[Both value and pointer can be changed]

Decision flow for interpreting const in pointer declarations.

const int*: Pointer to a Constant Integer

In the declaration const int* ptr;, const is to the left of int. This means the int value that ptr points to cannot be modified through ptr. However, ptr itself is not constant, meaning it can be reassigned to point to a different int (which must also be treated as constant through ptr). This is often referred to as a "pointer to const".

int value1 = 10;
int value2 = 20;

const int* ptr = &value1; // ptr points to a constant int

// *ptr = 15; // ERROR: cannot modify the value through ptr

std::cout << "Value pointed to by ptr: " << *ptr << std::endl; // Output: 10

ptr = &value2; // OK: ptr itself can be reassigned

std::cout << "New value pointed to by ptr: " << *ptr << std::endl; // Output: 20

Example of const int* behavior.

int* const: Constant Pointer to a Mutable Integer

With int* const ptr;, const is to the right of the *. This signifies that ptr itself is constant, meaning it cannot be reassigned to point to a different memory location after its initialization. However, the int value that ptr points to can be modified through ptr. This is often called a "const pointer".

int value = 10;
int another_value = 20;

int* const ptr = &value; // ptr is a constant pointer to an int

std::cout << "Initial value pointed to by ptr: " << *ptr << std::endl; // Output: 10

*ptr = 15; // OK: the value pointed to can be modified

std::cout << "Modified value pointed to by ptr: " << *ptr << std::endl; // Output: 15

// ptr = &another_value; // ERROR: ptr itself cannot be reassigned

Example of int* const behavior.

const int* const: Constant Pointer to a Constant Integer

The declaration const int* const ptr; combines both const placements. The first const (to the left of int) makes the int value pointed to constant. The second const (to the right of *) makes the pointer ptr itself constant. This means neither the value pointed to by ptr nor ptr itself can be modified after initialization. This is the most restrictive form.

int value = 10;
int another_value = 20;

const int* const ptr = &value; // ptr is a constant pointer to a constant int

std::cout << "Value pointed to by ptr: " << *ptr << std::endl; // Output: 10

// *ptr = 15; // ERROR: cannot modify the value through ptr
// ptr = &another_value; // ERROR: ptr itself cannot be reassigned

Example of const int* const behavior.

graph TD
    subgraph "Pointer Declarations"
        A["int* ptr"] -- "Mutable pointer, mutable value" --> B(No const)
        C["const int* ptr"] -- "Mutable pointer, constant value" --> D("const int")
        E["int* const ptr"] -- "Constant pointer, mutable value" --> F("const *ptr")
        G["const int* const ptr"] -- "Constant pointer, constant value" --> H("const int" AND "const *ptr")
    end

    subgraph "What can be changed?"
        B --> B1["ptr can change, *ptr can change"]
        D --> D1["ptr can change, *ptr CANNOT change"]
        F --> F1["ptr CANNOT change, *ptr can change"]
        H --> H1["ptr CANNOT change, *ptr CANNOT change"]
    end

Summary of pointer const declarations and their implications.

Mastering the nuances of const with pointers is fundamental for writing robust and maintainable C/C++ code. By explicitly declaring what parts of a pointer expression are constant, you leverage the compiler to catch potential errors at compile time, leading to more reliable software. Always consider the level of immutability required for your pointers and apply const accordingly.