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 same
void 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 achar[N][3]
&a[0]
, same asa
: address of the first element, which is itself achar[3]
&a[0][0]
: address of the first element of the first element, which is achar
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:
when it's the argument of the
&
(address-of) operator.when it's the argument of the
sizeof
operator.When it's a string literal of type
char [N + 1]
or a wide string literal of typewchar_t [N + 1]
(N
is the length of the string) which is used to initialize an array, as inchar str[] = "foo";
orwchar_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:
1
1
1
1
1
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*
.
Now to your quesiton...
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 ptr
std::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).
Related Topics
Remove Max Value from Simply-Connected List
Iterator Invalidation Rules For C++ Containers
How Does the Compilation/Linking Process Work
Single Quotes Vs. Double Quotes in C or C++
Convert Char to Int in C and C++
Finding Current Executable'S Path Without /Proc/Self/Exe
Opencv C++/Obj-C: Detecting a Sheet of Paper/Square Detection
Using Base Pointer Register in C++ Inline Asm
Order of Evaluation of Arguments Using Std::Cout
How Does Dereferencing of a Function Pointer Happen
Most Efficient Way to Compare a Variable to Multiple Values
How to Remove "Noise" from Gcc/Clang Assembly Output
Why Use Apparently Meaningless Do-While and If-Else Statements in Macros
C++ Delete - It Deletes My Objects But I Can Still Access the Data
Why Is Volatile Not Considered Useful in Multithreaded C or C++ Programming
If You Shouldn't Throw Exceptions in a Destructor, How to Handle Errors in It