33 / 163 · C++11 · 9 min
Reading pointer declarations
Read a complex declaration from the name outward, layer by layer according to how parentheses bind with the declarator. A pointer to an array, an array of pointers, and a function pointer are not equivalent. Which layer const qualifies also decides whether you can change the pointer or the target. Type aliases can make interfaces clearer.
In this lesson
Read layer by layer starting from the name
Find the name first, then identify the declaration structure that immediately surrounds it. In int* slots[3] the name first forms an array declaration, so slots is an array of three pointers. In int (*row)[3] parentheses bind the name to the star first, so row is a pointer to an array of three integers. They differ not only in syntax but also in the unit of stepping.
Functions work the same way: int* make() declares a function that returns a pointer to int; int (*apply)(int) declares a pointer to a function that takes an int and returns an int. Do not rely on a vague clockwise mnemonic. Parentheses determine the layer first; array and function suffixes then decide the type of that layer.
Locate const layer by layer
const int* p and int const* p both mean you must not modify the integer through p, but p may be repointed. int* const p fixes the pointer itself while still allowing the target to be modified. const int* const p restricts both; it does not mean no other alias can modify an originally non-const object.
Multi-level pointers must still be analyzed layer by layer. int** cannot be converted directly to const int**, otherwise you could, through the second-level pointer, stuff the address of a truly read-only object into a writable integer pointer. Adding one layer of const is not safe everywhere; having the compiler reject this conversion protects write permission.
Use aliases to express intent, not to hide complexity
using Unary = int (*)(int) extracts a function pointer into a readable name, so a callback interface can write Unary directly. An alias does not create a new type and does not change const semantics: if using P = int*, then const P is a constant pointer, not a pointer to a constant integer.
The example operates on an array of pointers, a pointer to an array, and a function pointer, and uses static_assert to confirm the alias meaning. Stars in a declaration list belong to their own declarators; in int* a, b only a is a pointer. In real maintenance, writing one name per declaration is usually more reliable than asking readers to keep peeling declarations apart.
Pitfalls
int (*p)[3]is notint**; the former points to a complete array. You cannot force-cast a two-dimensional array to disguise it as the latter.- const on a type alias qualifies the whole aliased type. You cannot treat the alias as a text macro, expand it, and then guess what is qualified.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <type_traits>
int increment(int value) { return value + 1; }
int main() {
int values[]{3, 5, 7};
int* slots[3]{&values[0], &values[1], &values[2]};
int (*row)[3] = &values;
using Unary = int (*)(int);
Unary apply = &increment;
assert(*slots[1] == 5);
assert((*row)[2] == 7);
assert(apply((*row)[0]) == 4);
using P = int*;
static_assert(std::is_same<const P, int* const>::value, "const qualifies pointer");
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread memory-pointer-declarations.cpp -o example && ./exampleExpected result
Expected: exit 0, no output; every assert holds.
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
Explain const int* table[2], and write a pointer to an array of two read-only integers.
Show a reference answer
table is an array of length two; each element is a pointer that may be repointed, but you cannot modify the integers through those pointers. The target declaration is const int (*p)[2]. You can also write using Row = const int[2]; Row* p = nullptr;. Parentheses bind p with the star so you do not get an array of pointers again.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.