When working with pointers in C++, the placement of the const keyword fundamentally alters which part of the declaration is read-only: the address stored in the pointer, the data at that address, or both.
Constant Pointers (Pointer to Non-Const)
A constant pointer is defined where the const qualifier follows the asterisk. The syntax typically looks like Type* const ptr;. This designates the pointer variable itself as immutable once initialized.
Key Characteristics:
- The memory address held by the pointer cannot be changed.
- The value stored at that memory location remains modifiable (unless the target type is also const).
Code Example:
#include <iostream>
int main() {
int alpha = 10;
int beta = 20;
// The pointer 'ptr' is a constant. It must point to alpha initially.
int* const ptr = α
// Modifying the value at the address is allowed
*ptr = 99;
std::cout << "Modified value: " << *ptr << std::endl;
// Attempting to change the address held by 'ptr' causes a compile error
// ptr = β // Error: assignment of read-only variable 'ptr'
return 0;
}
Pointesr to Constants (Constant Pointer Variable)
In this scenario, const precedes the fundamental type. The syntax appears as const Type* ptr; or equivalently Type const* ptr;. Here, the pointer variable is mutable, but the object it references is protected against modification.
Key Characteristics:
- The memory address can be updated to point elsewhere.
- The value accessed through dereferencing (
*ptr) cannot be modified.
Code Example:
#include <iostream>
int main() {
int x = 5;
int y = 8;
// 'ref' points to an integer, but the integer is treated as const
const int* ref = &x;
// Attempting to modify the value via the pointer triggers an error
// *ref = 10; // Error: discards qualifiers from pointed-to type
// Changing what the pointer points to is valid
ref = &y;
std::cout << "Value at new address: " << *ref << std::endl;
return 0;
}
Constant Pointers to Constants
Combining both rules results in a declaration where neither the address nor the data can be altered. The syntax places const on both sides of the asterisk: const Type* const ptr;.
Key Characteristics:
- The pointer address is fixed after initialization.
- The data content is immutable.
Code Example:
void processImmutableData(const int* const values, int count) {
for (int i = 0; i < count; ++i) {
// Accessing is fine, modifying is forbidden
std::cout << values[i] << " ";
}
// Attempting to change the parameter pointer or data inside fails
// values = nullptr; // Error: assignment of read-only variable
// values[i] = 0; // Error: assignment of read-only element
}
int main() {
int dataset[] = {100, 200, 300};
// Passing array name works as it decays to a pointer
processImmutableData(dataset, 3);
return 0;
}
Implications for Function Parameters
Correctly specifying pointer constness in function signatures ensures data integrity without unnecessary copying.
Example: Fixing a Value vs. Redirecting a Reference
// Setting the target value to zero using a constant pointer
void resetTarget(int* const target) {
// Valid: modifies data
if (target != nullptr) {
*target = 0;
}
// Invalid: cannot reassign target pointer
// target = nullptr;
}
// Passing data without risk of modification using const pointer
void logValues(const int* data, size_t length) {
for (size_t i = 0; i < length; ++i) {
// Valid: reading only
volatile int val = data[i];
}
}