Why Arrays Decay into Pointers in C
In the C programming language, array decay refers to the implicit conversion of an array identifier into a pointer to its first element during expression evaluation. This article examines the historical and architectural reasons behind this design choice, tracing its roots from the B language into C, detailing the efficiency benefits regarding memory and function arguments, and outlining the primary contexts where decay occurs alongside the critical exceptions where it does not.
Historical Origins from B and BCPL
The primary reason arrays decay into pointers in C is backward compatibility with its predecessor languages, BCPL and B. In B, an array declaration actually created two distinct entities: a block of contiguous memory for the elements, and an explicit pointer variable that stored the address of that block. Whenever an array identifier was referenced, the runtime accessed this stored pointer.
When Dennis Ritchie developed C, he introduced typed data and structures. Ritchie wanted to eliminate the memory overhead of maintaining a separate, physical pointer variable for every array, especially on memory-constrained systems like the PDP-11. His solution was to make the array identifier represent the memory block itself rather than an independent pointer variable. However, to preserve existing B coding patterns—particularly the relationship between arrays and pointer arithmetic—the C compiler was designed to generate an address to the array’s first element whenever the array name appeared in an expression.
Performance and Function Call Semantics
C is strictly a pass-by-value language. If arrays did not decay into pointers, passing an array to a function would require copying every single element onto the call stack. For large datasets, this would cause severe performance degradation and rapid stack exhaustion.
Array decay allows functions to accept an array argument efficiently:
void process_data(int *data, size_t size);When passing an array identifier to this function, the array decays
into a pointer of type int * holding the address of
data[0]. This mechanism achieves the performance benefits
of pass-by-reference without introducing explicit reference types into
the language grammar.
Unification with Pointer Arithmetic
Array decay is also fundamental to how C implements subscript notation. In C, the subscript operator is syntactically defined in terms of pointer arithmetic:
a[i] is identical to *(a + i)
For the expression a + i to be valid, a
must have a pointer type. Array decay automatically converts
a from type "array of T" to "pointer to
T", allowing the compiler to perform pointer offset
calculations based on the size of the underlying data type.
Contexts Where Arrays Do Not Decay
Array decay occurs in almost all expressions, but the C standard defines specific contexts where the array identifier preserves its true array type:
- The
sizeofOperator: Insizeof(arr), decay is inhibited so the operator returns the total memory footprint of the array in bytes, rather than the size of a pointer. - The Address-of Operator (
&): In&arr, the expression evaluates to the address of the entire array (typeT (*)[N]), rather than a pointer to a pointer. - String Literal Initialization: When initializing a
character array, such as
char str[] = "text";, the string literal does not decay into achar *; instead, its contents are copied directly into the allocated array memory. - The
_AlignofOperator: In C11 and later,_Alignof(arr)evaluates the alignment requirement of the entire array type, not a pointer.