Related
What is array to pointer decay? Is there any relation to array pointers?
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.
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 therefore can happen unexpectedly 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.
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 "15-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.
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.
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.
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("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
printf("%zu\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.
tl;dr: When you use an array you've defined, you'll actually be using a pointer to its first element.
Thus:
When you write arr[idx] you're really just saying *(arr + idx).
functions never really take arrays as parameters, only pointers - either directly, when you specify an array parameter, or indirectly, if you pass a reference to an array.
Sort-of exceptions to this rule:
You can pass fixed-length arrays to functions within a struct.
sizeof() gives the size taken up by the array, not the size of a pointer.
Try this code
void f(double a[10]) {
printf("in function: %d", sizeof(a));
printf("pointer size: %d\n", sizeof(double *));
}
int main() {
double a[10];
printf("in main: %d", sizeof(a));
f(a);
}
and you will see that the size of the array inside the function is not equal to the size of the array in main, but it is equal to the size of a pointer.
You probably heard that "arrays are pointers", but, this is not exactly true (the sizeof inside main prints the correct size). However, when passed, the array decays to pointer. That is, regardless of what the syntax shows, you actually pass a pointer, and the function actually receives a pointer.
In this case, the definition void f(double a[10] is implicitly transformed by the compiler to void f(double *a). You could have equivalently declared the function argument directly as *a. You could have even written a[100] or a[1], instead of a[10], since it is never actually compiled that way (however, you shouldn't do it obviously, it would confuse the reader).
Arrays are automatically passed by pointer in C. The rationale behind it can only be speculated.
int a[5], int *a and int (*a)[5] are all glorified addresses meaning that the compiler treats arithmetic and deference operators on them differently depending on the type, so when they refer to the same address they are not treated the same by the compiler. int a[5] is different to the other 2 in that the address is implicit and does not manifest on the stack or the executable as part of the array itself, it is only used by the compiler to resolve certain arithmetic operations, like taking its address or pointer arithmetic. int a[5] is therefore an array as well as an implicit address, but as soon as you talk about the address itself and place it on the stack, the address itself is no longer an array, and can only be a pointer to an array or a decayed array i.e. a pointer to the first member of the array.
For instance, on int (*a)[5], the first dereference on a will produce an int * (so the same address, just a different type, and note not int a[5]), and pointer arithmetic on a i.e. a+1 or *(a+1) will be in terms of the size of an array of 5 ints (which is the data type it points to), and the second dereference will produce the int. On int a[5] however, the first dereference will produce the int and the pointer arithmetic will be in terms of the size of an int.
To a function, you can only pass int * and int (*)[5], and the function casts it to whatever the parameter type is, so within the function you have a choice whether to treat an address that is being passed as a decayed array or a pointer to an array (where the function has to specify the size of the array being passed). If you pass a to a function and a is defined int a[5], then as a resolves to an address, you are passing an address, and an address can only be a pointer type. In the function, the parameter it accesses is then an address on the stack or in a register, which can only be a pointer type and not an array type -- this is because it's an actual address on the stack and is therefore clearly not the array itself.
You lose the size of the array because the type of the parameter, being an address, is a pointer and not an array, which does not have an array size, as can be seen when using sizeof, which works on the type of the value being passed to it. The parameter type int a[5] instead of int *a is allowed but is treated as int * instead of disallowing it outright, though it should be disallowed, because it is misleading, because it makes you think that the size information can be used, but you can only do this by casting it to int (*a)[5], and of course, the function has to specify the size of the array because there is no way to pass the size of the array because the size of the array needs to be a compile-time constant.
I might be so bold to think there are four (4) ways to pass an array as the function argument. Also here is the short but working code for your perusal.
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
using namespace std;
// test data
// notice native array init with no copy aka "="
// not possible in C
const char* specimen[]{ __TIME__, __DATE__, __TIMESTAMP__ };
// ONE
// simple, dangerous and useless
template<typename T>
void as_pointer(const T* array) {
// a pointer
assert(array != nullptr);
} ;
// TWO
// for above const T array[] means the same
// but and also , minimum array size indication might be given too
// this also does not stop the array decay into T *
// thus size information is lost
template<typename T>
void by_value_no_size(const T array[0xFF]) {
// decayed to a pointer
assert( array != nullptr );
}
// THREE
// size information is preserved
// but pointer is asked for
template<typename T, size_t N>
void pointer_to_array(const T (*array)[N])
{
// dealing with native pointer
assert( array != nullptr );
}
// FOUR
// no C equivalent
// array by reference
// size is preserved
template<typename T, size_t N>
void reference_to_array(const T (&array)[N])
{
// array is not a pointer here
// it is (almost) a container
// most of the std:: lib algorithms
// do work on array reference, for example
// range for requires std::begin() and std::end()
// on the type passed as range to iterate over
for (auto && elem : array )
{
cout << endl << elem ;
}
}
int main()
{
// ONE
as_pointer(specimen);
// TWO
by_value_no_size(specimen);
// THREE
pointer_to_array(&specimen);
// FOUR
reference_to_array( specimen ) ;
}
I might also think this shows the superiority of C++ vs C. At least in reference (pun intended) of passing an array by reference.
Of course there are extremely strict projects with no heap allocation, no exceptions and no std:: lib. C++ native array handling is mission critical language feature, one might say.
What is array to pointer decay? Is there any relation to array pointers?
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.
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 therefore can happen unexpectedly 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.
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 "15-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.
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.
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.
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("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
printf("%zu\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.
tl;dr: When you use an array you've defined, you'll actually be using a pointer to its first element.
Thus:
When you write arr[idx] you're really just saying *(arr + idx).
functions never really take arrays as parameters, only pointers - either directly, when you specify an array parameter, or indirectly, if you pass a reference to an array.
Sort-of exceptions to this rule:
You can pass fixed-length arrays to functions within a struct.
sizeof() gives the size taken up by the array, not the size of a pointer.
Try this code
void f(double a[10]) {
printf("in function: %d", sizeof(a));
printf("pointer size: %d\n", sizeof(double *));
}
int main() {
double a[10];
printf("in main: %d", sizeof(a));
f(a);
}
and you will see that the size of the array inside the function is not equal to the size of the array in main, but it is equal to the size of a pointer.
You probably heard that "arrays are pointers", but, this is not exactly true (the sizeof inside main prints the correct size). However, when passed, the array decays to pointer. That is, regardless of what the syntax shows, you actually pass a pointer, and the function actually receives a pointer.
In this case, the definition void f(double a[10] is implicitly transformed by the compiler to void f(double *a). You could have equivalently declared the function argument directly as *a. You could have even written a[100] or a[1], instead of a[10], since it is never actually compiled that way (however, you shouldn't do it obviously, it would confuse the reader).
Arrays are automatically passed by pointer in C. The rationale behind it can only be speculated.
int a[5], int *a and int (*a)[5] are all glorified addresses meaning that the compiler treats arithmetic and deference operators on them differently depending on the type, so when they refer to the same address they are not treated the same by the compiler. int a[5] is different to the other 2 in that the address is implicit and does not manifest on the stack or the executable as part of the array itself, it is only used by the compiler to resolve certain arithmetic operations, like taking its address or pointer arithmetic. int a[5] is therefore an array as well as an implicit address, but as soon as you talk about the address itself and place it on the stack, the address itself is no longer an array, and can only be a pointer to an array or a decayed array i.e. a pointer to the first member of the array.
For instance, on int (*a)[5], the first dereference on a will produce an int * (so the same address, just a different type, and note not int a[5]), and pointer arithmetic on a i.e. a+1 or *(a+1) will be in terms of the size of an array of 5 ints (which is the data type it points to), and the second dereference will produce the int. On int a[5] however, the first dereference will produce the int and the pointer arithmetic will be in terms of the size of an int.
To a function, you can only pass int * and int (*)[5], and the function casts it to whatever the parameter type is, so within the function you have a choice whether to treat an address that is being passed as a decayed array or a pointer to an array (where the function has to specify the size of the array being passed). If you pass a to a function and a is defined int a[5], then as a resolves to an address, you are passing an address, and an address can only be a pointer type. In the function, the parameter it accesses is then an address on the stack or in a register, which can only be a pointer type and not an array type -- this is because it's an actual address on the stack and is therefore clearly not the array itself.
You lose the size of the array because the type of the parameter, being an address, is a pointer and not an array, which does not have an array size, as can be seen when using sizeof, which works on the type of the value being passed to it. The parameter type int a[5] instead of int *a is allowed but is treated as int * instead of disallowing it outright, though it should be disallowed, because it is misleading, because it makes you think that the size information can be used, but you can only do this by casting it to int (*a)[5], and of course, the function has to specify the size of the array because there is no way to pass the size of the array because the size of the array needs to be a compile-time constant.
I might be so bold to think there are four (4) ways to pass an array as the function argument. Also here is the short but working code for your perusal.
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
using namespace std;
// test data
// notice native array init with no copy aka "="
// not possible in C
const char* specimen[]{ __TIME__, __DATE__, __TIMESTAMP__ };
// ONE
// simple, dangerous and useless
template<typename T>
void as_pointer(const T* array) {
// a pointer
assert(array != nullptr);
} ;
// TWO
// for above const T array[] means the same
// but and also , minimum array size indication might be given too
// this also does not stop the array decay into T *
// thus size information is lost
template<typename T>
void by_value_no_size(const T array[0xFF]) {
// decayed to a pointer
assert( array != nullptr );
}
// THREE
// size information is preserved
// but pointer is asked for
template<typename T, size_t N>
void pointer_to_array(const T (*array)[N])
{
// dealing with native pointer
assert( array != nullptr );
}
// FOUR
// no C equivalent
// array by reference
// size is preserved
template<typename T, size_t N>
void reference_to_array(const T (&array)[N])
{
// array is not a pointer here
// it is (almost) a container
// most of the std:: lib algorithms
// do work on array reference, for example
// range for requires std::begin() and std::end()
// on the type passed as range to iterate over
for (auto && elem : array )
{
cout << endl << elem ;
}
}
int main()
{
// ONE
as_pointer(specimen);
// TWO
by_value_no_size(specimen);
// THREE
pointer_to_array(&specimen);
// FOUR
reference_to_array( specimen ) ;
}
I might also think this shows the superiority of C++ vs C. At least in reference (pun intended) of passing an array by reference.
Of course there are extremely strict projects with no heap allocation, no exceptions and no std:: lib. C++ native array handling is mission critical language feature, one might say.
I was wondering how *(&array + 1) actually works. I saw this as an easy way to calculate the array length and want to understand it properly before using it. I'm not very experienced with pointer arithmetic, but with my understanding &array gives the address of the first element of the array. (&array + 1) would go to end of the array in terms of address. But shouldn't *(&array + 1) give the value, which is at this address. Instead it prints out the address. I would really appreciate your help to get the pointer stuff clear in my head.
Here is the simple example I'm working on:
int numbers[] = {5,8,9,3,4,6,1};
int length = *(&numbers + 1) - numbers;
(This answer is for C++.)
&numbers is a pointer to the array itself. It has type int (*)[7].
&numbers + 1 is a pointer to the byte right after the array, where another array of 7 ints would be located. It still has type int (*)[7].
*(&numbers + 1) dereferences this pointer, yielding an lvalue of type int[7] referring to the byte right after the array.
*(&numbers + 1) - numbers: Using the - operator forces both operands to undergo the array-to-pointer conversion, so pointers can be subtracted. *(&numbers + 1) is converted to an int* pointing at the byte after the array. numbers is converted to an int* pointing at the first byte of the array. Their difference is the number of ints between the two pointers---which is the number of ints in the array.
Edit: Although there's no valid object pointed to by &numbers + 1, this is what's called a "past the end" pointer. If p is a pointer to T, pointing to a valid object of type T, then it's always valid to compute p + 1, even though *p may be a single object, or the object at the end of an array. In that case, you get a "past the end" pointer, which does not point to a valid object, but is still a valid pointer. You can use this pointer for pointer arithmetic, and even dereference it to yield an lvalue, as long as you do not try to read or write through that lvalue. Note that you can only go one byte past-the-end of an object; attempting to go any further leads to undefined behaviour.
The expression &numbers gives you the address of the array, not the first member (although numerically they are the same). The type of this expression is int (*)[7], i.e. a pointer to an array of size 7.
The expression &numbers + 1 adds sizeof(int[7]) bytes to the address of array. The resulting pointer points right after the array.
The problem however is when you then dereference this pointer with *(&numbers + 1). Dereferencing a pointer that points one element past the end of an array invokes undefined behavior.
The proper way to get the number of elements of an array is sizeof(numbers)/sizeof(numbers[0]). This assumes that the array was defined in the current scope and is not a parameter to a function.
but with my understanding &array gives the address of the first element of the array.
This understanding is misleading. &array gives the address of the array. Sure, the value of that address is the same same as the first element, but the type of the expression is different. The type of the expression &array is "pointer to array of N elements of type T" (where N is the length that you're looking for and T is int).
But shouldn't *(&array + 1) give the value, which is at this address.
Well yes... but it's here that the type of the expression becomes important. Indirecting a pointer to an array (rather than pointer to an element of the array) will result in the array itself.
In the subtraction expression, both array operands decay into pointer to first element. Since the subtraction uses decayed pointers, the unit of the pointer arithmetic is in terms of the element size.
I saw this as an easy way to calculate the array length
There are easier ways:
std::size(numbers)
And in C:
sizeof(numbers)/sizeof(numbers[0])
This question already has answers here:
How come an array's address is equal to its value in C?
(6 answers)
Closed 7 years ago.
As a beginner programmer I am dealing with some simple problems related to Pointers. In the following code I found the value of *a and a are same in hexadecimal. But I can't understand the reason.
#include <stdio.h>
#include <stdlib.h>
main(){
int a[5][5];
a[0][0] = 1;
printf("*a=%p a=%p \n", *a, a);
return 0;
}
Here is the output:
*a=0x7ffddb8919f0 a=0x7ffddb8919f0
An array and its first element have the same address.:)
For this declaration
int a[5][5];
expression a used in the printf call is implicitly converted to the pointer to its first element. Expression *a yields the first element of the array that is in turn a one-dimensional array that also is converted to a pointer to its first element.
Thus expressions a and *a have the same value as expression &a[0][0]
In C and C++ languages values of array type T [N] are implicitly converted to values of pointer type T * in most contexts (with few exceptions). The resultant pointer points to the first element of the original array (index 0). This phenomenon is informally known as array type decay.
printf argument is one of those contexts when array type decay happens.
A 2D array of type int [5][5] is nothing else than an "1D array of 1D arrays", i.e. it is an array of 5 elements, with each element itself being an array of 5 ints.
The above array type decay rule naturally applies to this situation.
The expression a, which originally has array type int [5][5], decays to a pointer of type int (*)[5]. The pointer points to element a[0], which is the beginning of sub-array a[0] in memory. This is the first pointer you print.
The expression *a is a dereference operator applied to sub-expression a. Sub-expression a in this context behaves in exactly the same way as before: it decays to pointer of type int (*)[5] that points to a[0]. Thus the result of *a is a[0] itself. But a[0] is also an array. It is an array of int[5] type. It is also subject to array type decay. It decays to pointer of type int *, which points to the first element of a[0], i.e. to a[0][0]. This is the second pointer you print.
The reason both pointer values are the same numerically is that the beginning of sub-array a[0] corresponds to the same memory location as element a[0][0].
a can be considered a pointer to a pointer to an int (in reality, it's an array of array of int, but close enough).
So a and *a both point to the same address (which happens to be a[0][0]).
*a is still a pointer, and a[0] is the same address as a[0][0].
I have a question about syntax of pointer to arrays.
Well we know arrays are pointers themselves(what our uni professor said) so why when we point to them with another pointer (which would be a pointer to pointer) we use this syntax:
int array[10];
int *pointer = array;
Instead of this syntax:
int array[10];
int **pointer = &array;
Although i know this would be correct using malloc but why not in the normal way, is it a compiler or syntax thing or i am wrong somewhere else??
Tell your professor they are wrong. Arrays are not pointers. Arrays can decay to pointers, but they are not pointers.
int* pointer = array; declares a pointer that points to the first element in array.
int** pointer = &array; is not correct. As mentioned by jschultz410 in the comments, the type of &array is not int**, it is int (*)[10] aka a pointer to an array of 10 ints, which cannot decay to int**.
Well we know arrays are pointers themselves
No. Arrays are not pointers. Arrays are arrays. Except when it is the operand of the sizeof or the unary & operator, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer toT" and the value of the expression will be the address of the first element of the array. However, no storage is set aside for a pointer in addition to the array elements themselves.
So, given the declaration
int array[10];
the type of the expression array is "10-element array of int"; unless array is the operand of sizeof or unary &, it will decay to type int *. So
int *ptr = array;
works.
The type of &array is not int **; the type is int (*)[10], or "pointer to 10-element array of int". You'd declare and initialize such a pointer as
int (*ptr)[10] = &array;
First of all this definition of a pointer
int array[10];
int **pointer = &array;
is invalid. In the right side of the declaration there is an expression having type int ( * )[10] while in the left side there is identifier of type int **. There is no implicit conversion between pointers int ( * )[10] and int ** . So the compiler shall issue a diagnostic message.
The correct definition will look
int array[10];
int ( *pointer )[10] = &array;
Now we can consider what is the difference between these two definitions
int array[10];
int *pointer = array;
and
int array[10];
int ( *pointer )[10] = &array;
In the first case the size of the object pointed to by pointer pointer is equal to sizeof( int ). So if to use the pointer arithmetic then after evaluation of expression ++pointer the value in the pointer will be increased by sizeof( int ) bytes. If for example sizeof( int ) is equal to 4 then the value in the pointer will be increased by 4.
In the second case the size of the object pointed to by pointer pointer is equal to 10 * sizeof( int ) that is if sizeof( int ) is equal to 4 then the size of the object is equal to 40. So if the pointer will be increased ++pointer its value will be increased by 40.
Also dereferencing the pointer in the first case will give you an object of type int while dereferencing the pointer in the second case will give you an object of type int[10] that is an array.
And arrays are not pointers. Simply they are usually converted to pointers to their first elements in expressions. From the C Standard
3 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.
If you write for example
int array[10];
int *pointer = array;
then sizeof( array ) is not equal to sizeof( pointer ) though you can use the common syntax to access elements of the array:
array[ i ] and pointer[ i ] and will get the same result..