what is decaying of array? is there any relation to the array pointers?
It's when array rots and is being pointed at ;-)
Actually, it's just that if you want to pass an array somewhere, but the pointer is passed instead (because who the hell would pass the whole array for you), people say that poor array decayed to pointer.
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.
When you pass an array into a function, either directly or with an explicit pointer to that array, it has decayed functionality, in that you lose the ability to call sizeof() on that item, because it essentially becomes a pointer. This is why it's preferred to pass by reference (among other reasons).
Three ways to pass in an array:
void by_value(const T array[])
void by_pointer(const T* const array)
void by_reference(const T (&array)[U])
Only the last, the reference example, will give proper sizeof() info.
Edited to add: Note: it's not really "by value", just idiomatically so. The purpose of the three functions is to show the various "techniques" which don't maintain the full functionality of an array, i.e. sizeof(), but instead lead to "decay".
Arrays are basically the same as pointers in C/C++, but not quite. Once you convert an array:
const int a[] = { 2, 3, 5, 7, 11 };
into a pointer (which works without casting, and can happen implicitly in some cases):
const int* p = a;
you lose the ability of the sizeof
operator to count elements in the array:
assert( sizeof(p) != sizeof(a) ); // sizes are not equal
This lost ability is referred to as "decay".
For more details, check out this article about array decay.
Array decaying means that, when an array is passed as a parameter to a function, it's treated identically to ("decays to") a pointer.
void do_something(int *array) {
// We don't know how big array is here, because it's decayed to a pointer.
printf("%i\n", sizeof(array)); // always prints 4 on a 32-bit machine
}
int main (int argc, char **argv) {
int a[10];
int b[20];
int *c;
printf("%i\n", sizeof(a)); //prints 40 on a 32-bit machine
printf("%i\n", sizeof(b)); //prints 80 on a 32-bit machine
printf("%i\n", sizeof(c)); //prints 4 on a 32-bit machine
do_something(a);
do_something(b);
do_something(c);
}
There are two complications or exceptions to the above.
First, when dealing with multidimensional arrays in C and C++, only the first dimension is lost. This is because arrays are layed out contiguously in memory, so the compiler must know all but the first dimension to be able to calculate offsets into that block of memory.
void do_something(int array[][10])
{
// We don't know how big the first dimension is.
}
int main(int argc, char *argv[]) {
int a[5][10];
int b[20][10];
do_something(a);
do_something(b);
return 0;
}
Second, in C++, you can use templates to deduce the size of arrays. Microsoft uses this for the C++ versions of Secure CRT functions like strcpy_s, and you can use a similar trick to reliably get the number of elements in an array.
Arrays, in C, have no value.
Wherever the value of an object is expected but the object is an array, the address of its first element is used instead, with type pointer to (type of array elements)
.
In a function, all parameters are passed by value (arrays are no exception). When you pass an array in a function it "decays into a pointer" (sic); when you compare an array to something else, again it "decays into a pointer" (sic); ...
void foo(int arr[]);
Function foo expects the value of an array. But, in C, arrays have no value! So foo
gets instead the address of the first element of the array.
int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }
In the comparison above, arr
has no value, so it becomes a pointer. It becomes a pointer to int. That pointer can be compared with the variable ip
.
In the array indexing syntax you are used to seeing, again, the arr is 'decayed to a pointer'
arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */
The only times an array doesn't decay into a pointer are when it is the operand of the sizeof operator, or the & operator (the 'address of' operator), or as a string literal used to initialize a character array.
Here's what the standard says (C99 6.3.2.1/3 - Other operands - Lvalues, arrays, and function designators):
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.
This means that pretty much anytime the array name is used in an expression, it is automatically converted to a pointer to the 1st item in the array.
Note that function names act in a similar way, but function pointers are used far less and in a much more specialized way that it doesn't cause nearly as much confusion as the automatic conversion of array names to pointers.
The C++ standard (4.2 Array-to-pointer conversion) loosens the conversion requirement to (emphasis mine):
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to an rvalue of type “pointer to T.”
So the conversion doesn't have to happen like it pretty much always does in C (this lets functions overload or templates match on the array type).
This is also why in C you should avoid using array parameters in function prototypes/definitions (in my opinion - I'm not sure if there's any general agreement). They cause confusion and are a fiction anyway - use pointer parameters and the confusion might not go away entirely, but at least the parameter declaration isn't lying.
"Decay" refers to the implicit conversion of an expression from an array type to a pointer type. In most contexts, when the compiler sees an array expression it converts the type of the expression from "N-element array of T" to "pointer to T" and sets the value of the expression to the address of the first element of the array. The exceptions to this rule are when an array is an operand of either the sizeof
or &
operators, or the array is a string literal being used as an initializer in a declaration.
Assume the following code:
char a[80];
strcpy(a, "This is a test");
The expression a
is of type "80-element array of char" and the expression "This is a test" is of type "16-element array of char" (in C; in C++ string literals are arrays of const char). However, in the call to strcpy()
, neither expression is an operand of sizeof
or &
, so their types are implicitly converted to "pointer to char", and their values are set to the address of the first element in each. What strcpy()
receives are not arrays, but pointers, as seen in its prototype:
char *strcpy(char *dest, const char *src);
This is not the same thing as an array pointer. For example:
char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;
Both ptr_to_first_element
and ptr_to_array
have the same value; the base address of a. However, they are different types and are treated differently, as shown below:
a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]
Remember that the expression a[i]
is interpreted as *(a+i)
(which only works if the array type is converted to a pointer type), so both a[i]
and ptr_to_first_element[i]
work the same. The expression (*ptr_to_array)[i]
is interpreted as *(*a+i)
. The expressions *ptr_to_array[i]
and ptr_to_array[i]
may lead to compiler warnings or errors depending on the context; they'll definitely do the wrong thing if you're expecting them to evaluate to a[i]
.
sizeof a == sizeof *ptr_to_array == 80
Again, when an array is an operand of sizeof
, it's not converted to a pointer type.
sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
is on your platform
ptr_to_first_element
is a simple pointer to char.