Ch5.7: C‑style Array Decay
Overview
C‑style arrays have a special behavior called array decay. In most expressions, a C‑style array automatically converts (or “decays”) into a pointer to its first element. This is one of the most important rules in C++, and it explains why arrays and pointers often appear interchangeable even though they are not the same.
In this chapter, you will learn:
- what C‑style array decay is
- when decay happens
- when decay does not happen
- why
sizeof(a)andsizeof(p)differ - why function parameters cannot take arrays by value
- how decay interacts with pointer arithmetic
- how to use
::std::ranges::begin(a)and::std::ranges::end(a)
1. What is C‑style array decay?
When a C‑style array is used in most expressions, it automatically converts into a pointer to its first element.
int a[3]{10, 20, 30};
int *p = a; // array decays to pointer to a[0]
After decay:
abecomes&a[0]- the array’s size information is lost
a (array):
Address: 1000
Memory: [ 10 ][ 20 ][ 30 ]
p (pointer):
Address: 2000
Memory: [ 1000 ] ← pointer to a[0]
2. Why does decay happen?
C‑style arrays cannot be copied or assigned. They also cannot be passed by value to functions.
To allow arrays to be used in expressions and function calls, C++ automatically converts them to pointers.
3. When decay happens
Decay happens in most expressions, including:
- assignment to a pointer
- passing an array to a function
- comparison
- pointer arithmetic
int a[3]{10, 20, 30};
int *p = a; // decay
int *q = a + 1; // decay, then pointer arithmetic
bool same = (a == p); // decay, then compare pointers
4. When decay does NOT happen
Decay does not happen in the following cases:
- sizeof(a)
- &a (address‑of the entire array)
- decltype(a)
- template argument deduction in some contexts
int a[3]{10, 20, 30};
std::size_t s1 = sizeof(a); // 3 * sizeof(int)
std::size_t s2 = sizeof(a[0]); // sizeof(int)
int *p = a; // decay
std::size_t s3 = sizeof(p); // size of pointer, NOT array
Key rule:
sizeof(a) gives the size of the entire array.
sizeof(p) gives the size of a pointer.
5. Decay in function parameters
You cannot pass a C‑style array by value to a function. Instead, the array decays to a pointer.
void f(int *p); // receives pointer, not array
int a[3]{10, 20, 30};
f(a); // a decays to &a[0]
Even if you write:
void g(int a[3]);
it is still interpreted as:
void g(int *a); // array size is ignored
6. Decay and pointer arithmetic
After decay, the pointer behaves like any other pointer to the first element.
int a[3]{10, 20, 30};
int *p = a; // decay
int *q = p + 2;
int x = *q; // 30
This is why array indexing works:
a[i] // means *(a + i)
Because a decays to a pointer.
7. Using ::std::ranges::begin(a) and ::std::ranges::end(a)
Instead of relying on decay, modern C++ provides explicit functions to obtain iterators for C‑style arrays:
#include
int a[3]{10, 20, 30};
int *first = ::std::ranges::begin(a);
int *last = ::std::ranges::end(a); // one past the end
These functions do not evaluate a[N].
They compute the correct pointers safely:
::std::ranges::begin(a)→&a[0]::std::ranges::end(a)→&a[0] + N
This is the preferred modern way to obtain iterators for C‑style arrays.
8. Why decay can be dangerous
After decay, the pointer no longer knows the array’s size. This can lead to:
- out‑of‑bounds pointer arithmetic
- incorrect
sizeofusage - loss of type information
For example:
int a[3]{10, 20, 30};
int *p = a;
std::size_t s = sizeof(p); // size of pointer, NOT array
This is a common source of bugs.
Key takeaways
- C‑style arrays automatically decay to pointers in most expressions.
- Decay converts
ainto&a[0]. - Decay loses the array’s size information.
sizeof(a)gives the array size;sizeof(p)gives pointer size.- Function parameters cannot take arrays by value; they receive pointers.
- Array indexing
a[i]is defined as*(a + i)because of decay. - Use
::std::ranges::begin(a)and::std::ranges::end(a)to obtain array iterators. - Pointers are the iterators of C‑style arrays.
- Decay can be dangerous because the pointer no longer knows the array size.