﻿ What Is Array to Pointer Decay - ITCodar

# What Is Array to Pointer Decay

## What is array to pointer decay?

It's said that arrays "decay" into pointers. A C++ array declared as `int numbers [5]` cannot be re-pointed, i.e. you can't say `numbers = 0x5a5aff23`. More importantly the term decay signifies loss of type and dimension; `numbers` decay into `int*` by losing the dimension information (count 5) and the type is not `int [5]` any more. Look here for cases where the decay doesn't happen.

If you're passing an array by value, what you're really doing is copying a pointer - a pointer to the array's first element is copied to the parameter (whose type should also be a pointer the array element's type). This works due to array's decaying nature; once decayed, `sizeof` no longer gives the complete array's size, because it essentially becomes a pointer. This is why it's preferred (among other reasons) to pass by reference or pointer.

Three ways to pass in an array1:

``void by_value(const T* array)   // const T array[] means the samevoid by_pointer(const T (*array)[U])void by_reference(const T (&array)[U])``

The last two will give proper `sizeof` info, while the first one won't since the array argument has decayed to be assigned to the parameter.

1 The constant U should be known at compile-time.

## Array to pointer decay and passing multidimensional arrays to functions

You got it slightly wrong: `moreThings` also decays to a pointer to the first element, but since it is an array of an array of chars, the first element is an "array of 8 chars". So the decayed pointer is of this type:

``char (*p)[8] = moreThings;``

The value of the pointer is of course the same as the value of `&moreThings[0][0]`, i.e. of the first element of the first element, and also the same of `&a`, but the type is a different one in each case.

Here's an example if `char a[N][3]`:

``+===========================+===========================+====|+--------+--------+-------+|+--------+--------+-------+||| a[0,0] | a[0,1] | a[0,2]||| a[1,0] | a[1,1] | a[1,2]|| ...|+--------+--------+-------+++--------+--------+-------++ ...|            a[0]           |            a[1]           |+===========================+===========================+====                                    a^^^||+-- &a[0,0]|+-----&a[0]+-------&a``
• `&a`: address of the entire array of arrays of chars, which is a `char[N][3]`

• `&a[0]`, same as `a`: address of the first element, which is itself a `char[3]`

• `&a[0][0]`: address of the first element of the first element, which is a `char`

This demonstrates that different objects may have the same address, but if two objects have the same address and the same type, then they are the same object.

## Is the array to pointer decay changed to a pointer object?

"But `a` itself is not pointing to another region of memory, it IS the region of memory itself.

"So when the compiler converts it to a pointer, does it save it (like `p`) somewhere in memory or it's an implicit conversion?"

It is an implicit conversion. The compiler does not implement the creation of a separate pointer object in memory (which you can f.e. assign in any manner with a different memory address) to hold the address of the first element.

The standard states (emphasize mine):

"Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined."

Source: ISO/IEC 9899:2018 (C18), 6.3.2.1/4

The array is converted to an expression of pointer type, it is not an `lvalue`.

The compiler just evaluates `a` to `&a[0]` (pointer to `a[0]`).

"I understand that array names are converted to pointers."

An array does not always convert to a pointer to its first element. Look at the first part of the quote above. F.e. when used as `&a`, `a` does not decay to a pointer to its first element. Rather it gains a pointer to the whole array `int (*)[3]`.

## why does the array decay to a pointer in a template function

Because arrays can not be passed by value as a function parameter.

When you pass them by value they decay into a pointer.

In this function:

``template <class T>void f(T buff) {``

T can not be `char (&buff)[3]` as this is a reference. The compiler would have tried `char (buff)[3]` to pass by value but that is not allowed. So to make it work arrays decay to pointers.

Your second function works because here the array is passed by reference:

``template <class T>void f1(T& buff) {// Here T& => char (&buff)[3]``

## Exceptions to array decaying into a pointer?

Sure.

In C99 there are three fundamental cases, namely:

1. when it's the argument of the `&` (address-of) operator.

2. when it's the argument of the `sizeof` operator.

3. When it's a string literal of type `char [N + 1]` or a wide string literal of type `wchar_t [N + 1]` (`N` is the length of the string) which is used to initialize an array, as in `char str[] = "foo";` or `wchar_t wstr[] = L"foo";`.

Furthermore, in C11, the newly introduced `alignof` operator doesn't let its array argument decay into a pointer either.

In C++, there are additional rules, for example, when it's passed by reference.

## If arr[3] is an array and ptr is a pointer, then why do arr and &arr give same result but ptr and &ptr don't [duplicate]

Arrays are not pointers!

Arrays do decay to a pointer to their first element in all sorts of circumstances. For example `std::cout << arr;` actually prints the memory address of the first element of the array. `std::cout << &arr;` prints the memory address of the array. As the address of the first element is the same as the address of the array you see the same value.

However, just because they have the same value, does not mean they are the same. `arr` can decay to a `int*`, while `&arr` is a pointer to an array, a `int(*)[3]`.

I hope the following will help to clear things up a little:

``#include <iostream>#include <type_traits>void make_it_decay(int x[]) {    std::cout << std::is_same_v< decltype(x), int*> << "\n";}int main() {    int arr[3] = {1,2,3};    //std::cout << (arr == &arr) << "\n";  // does not compile !    std::cout << (arr == &(arr[0])) << "\n";    std::cout << std::is_same_v< decltype(arr), int[3]> << "\n";    std::cout << std::is_same_v< decltype(&arr),int(*)[3]> << "\n";    std::cout << std::is_same_v< decltype(&arr[0]), int* > << "\n";    make_it_decay(arr);}``

output:

``11111``

I use `decltype` to infer the type of certain expressions and `std::is_same_v` to see if the expressions are of same type.

`arr` is of type `int[3]`. It is an array. It is not a pointer.

`&arr` is the address of the array. It is a pointer to an array with three elements, a `int(*)[3]`.

`&arr[0]` even though it has the same value as `&arr` is of different type. It is a pointer to `int`, an `int*`.

When we pass `arr` to a function then it decays to a pointer to the first element of the array. And we can see that inside the function `x` is `int*`.

Above I tried to lay out what happens when you write `std::cout << arr`. Pointers are different, because ... well arrays are not pointers.

``std::cout << ptr;  // prints the value ptrstd::cout << &ptr; // prints the address of ptr``

Perhaps some visualization helps. The difference in types gets most apparent when incrementing the pointers

`` ------------------- | arr             | ------------------- |  1  |  2  |  3  | -------------------   ^       ^         ^   &arr    |         &arr + 1    &arr[0] |           &arr[0] + 1``

## Array reference binding vs. array-to-pointer conversion with templates

The second overload is more specialized than the first one during partial ordering of function templates.

According to [temp.deduct.partial]/5 the reference on `T &t` of the first overload is ignored during template argument deduction performed for partial ordering. The following paragraphs distinguish based on reference/value category only if both parameters are reference types.

Then `T` of the first overload can always deduce against a type `A*` invented from the parameter of the second overload, but `T*` of the second overload can't deduce against a type `A` invented from the parameter of the first overload.

Therefore the second overload is more specialized and is chosen.

With `T (&t)[4]` argument deduction in both directions will fail because deduction of `T[4]` against `A*` will fail and so will deduction of `T*` against `A[4]`. Array-to-pointer decay of the array type is specified for template argument deduction for a function call but not for template argument deduction for partial ordering. See also active CWG issue 402.

So neither template will be more specialized in this case and the partial ordering tiebreaker does not apply.

The array-to-pointer conversion is not relevant. It is not considered any worse than the identity conversion sequence (see [over.ics.rank]/3.2.1 excluding lvalue transformations which array-to-pointer conversions are).