Array to Pointer Decay in C++ Explained
In C++, pointer decay refers to the implicit conversion where an array loses its dimension and size information and reduces to a pointer to its first element. While arrays and pointers are distinct types, C++ inherits C-style array behaviors where arrays automatically convert into pointers in most expressions. This article explains the mechanics of pointer decay, identifies the exact circumstances under which it occurs, outlines the critical exceptions where it does not happen, and discusses modern C++ practices to prevent it.
What Is Pointer Decay?
An array in C++ represents a contiguous block of memory with a known,
fixed size encoded in its type (for example, int[10]).
Pointer decay is the loss of this type and size information when the
array type T[N] implicitly converts into a pointer type
T*.
When an array decays:
- The resulting pointer points to the first element
(
&arr[0]). - The compile-time size information of the array is lost.
- Operations like
sizeofevaluate the size of the pointer itself (typically 4 or 8 bytes) rather than the total memory occupied by the array elements.
Circumstances When Array Decay Occurs
Array decay occurs automatically in most contexts where an array is used in an expression:
1. Passing an Array to a Function by Value
When an array is passed by value to a function, it automatically decays into a pointer. Even if the function parameter is declared with array syntax, the compiler adjusts it to a pointer:
void printValues(int arr[]) { // Adjusted by compiler to: void printValues(int* arr)
// sizeof(arr) returns sizeof(int*), not the array size
}
int numbers[5] = {1, 2, 3, 4, 5};
printValues(numbers); // 'numbers' decays to int*2. Assignment to a Pointer
Assigning an array to a compatible pointer type triggers implicit decay:
int data[10];
int* ptr = data; // 'data' decays to int* pointing to &data[0]3. Pointer Arithmetic and Indexing
Using arithmetic operators on an array identifier triggers decay:
int data[5] = {10, 20, 30, 40, 50};
int second = *(data + 1); // 'data' decays to int* before addition4. Type Deduction with
auto
When an array is used to initialize a variable declared with
auto (by value), the deduced type is a pointer:
int arr[4] = {1, 2, 3, 4};
auto p = arr; // 'p' is deduced as int*, not int[4]5. Ternary Operator and Function Returns
If an array is used as an operand in conditional expressions, it decays:
int a[3] = {1, 2, 3};
int b[3] = {4, 5, 6};
int* chosen = condition ? a : b; // Both arrays decay to int*When Arrays Do Not Decay
There are specific situations where the array preserves its complete type information:
The
sizeofOperator:sizeof(arr)evaluates the total size in bytes of the entire array, not the pointer size.The Address-of Operator (
&):&arrproduces a pointer to the entire array (typeT(*)[N]), not a pointer to an element (T*).Pass by Reference: Binding an array to a reference (
T(&)[N]) retains the exact array bounds:template <std::size_t N> void printSize(int (&arr)[N]) { // N is preserved at compile time; no decay occurs }decltypeSpecifier:decltype(arr)yields the original array typeT[N].String Literal Initialization: Initializing a character array with a string literal (
char str[] = "hello";) copies the contents into the array without decay.
How to Prevent Pointer Decay in Modern C++
To eliminate the risks of pointer decay—such as buffer overflows and silent loss of bounds checking—modern C++ provides standard alternatives:
std::array: A zero-overhead wrapper around C-style arrays that behaves as a standard container, preserving size semantics and preventing implicit pointer decay.std::vector: A dynamically-sized alternative that manages its own memory and retains size information.std::span(C++20): A non-owning view over contiguous sequences that bundles the pointer and the size together, providing safe parameter passing without losing bounds information.