What is the difference between const int*, const int * const, and int * const?
Categories:
Demystifying C/C++ Pointer Constants: 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.
const int* when you want to pass a pointer to a function and ensure that the function does not modify the original data. This is a common practice for function parameters.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.
const int* and int const* are semantically identical (both mean 'pointer to a constant int'), const int* is the more common and generally preferred style for readability. The rule 'const applies to what's on its left' still holds, as int const is equivalent to const int.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"]
endSummary 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.