#include <stdio.h>
int main() {
int arr[3] = { 1, 2, 3 };
int *p = arr;
int (*r)[3] = arr;
printf("%u %u", p, r);
printf("\n%d %d %d", p[0], p[1], p[2]);
printf("\n%d %d %d", r[0], r[1], r[2]);
printf("\n%d %d %d", *r[0], *r[1], *r[2]);
printf("\n%d %d %d", (*r)[0], (*r)[1], (*r)[2]);
}
Output:
1483745184 1483745184
1 2 3
1483745184 1483745196 1483745208
1 0 -647513344
1 2 3
As you can see p
and r
contains same address, then
- why does
p[0]
work butr[0]
doesn't? - what goes behind
p[0]
? - why does
(*r)[0]
work but others don't?
CodePudding user response:
The difference between these two declarations
int *p = arr;
int (*r)[3] = arr;
is that in the second declaration there is used a wrong initializer. The array arr
used as an initializer is implicitly converted to pointer to its first element of the type int *
. So in the second declaration the initialized object and the initializer have different pointer types and there is no implicit conversion between the types.
To make the second declaration correct you need to write
int (*r)[3] = &arr;
Now the both pointers p and r stores the same value: the address of the extent of memory occupied by the array but have different types.
For example if you will write
printf( "sizeof( *p ) = %zu\n", sizeof( *p ) );
printf( "sizeof( *r ) = %zu\n", sizeof( *r ) );
then the first call will output the size of an object of the type int
that is equal to 4
while the second call will output the size of the whole array of the type int[3]
that is equal to 12
.
In this your call of printf
printf("%u %u", p, r);
there are used incorrect conversion specifiers with pointers. Instead you have to write
printf("%p %p", ( void * )p, ( void * )r);
The expressions r[0], r[1], r[2] have the type int[3]. Used in this call
printf("\n%d %d %d", r[0], r[1], r[2]);
they as it was mentioned are implicitly converted to pointers to their first elements. But these arrays except the array r[0] that denotes the array arr do not exist. So there takes place an access to memory beyond the array arr,
You could write
printf( "\n%p\n", ( void * )r[0] );
and this call will be equivalent to
printf("\n%p\n", ( void * )arr );
This call of printf
printf("\n%d %d %d", *r[0], *r[1], *r[2]);
is also incorrect because arrays r[1]
and r[2]
of the type int[3]
do not exist.
This call of printf
printf("\n%d %d %d", (*r)[0], (*r)[1], (*r)[2]);
outputs elements of the array arr
due to using the expression *r
.
CodePudding user response:
The difference between r
and p
is solely in the type.
The first thing to note is that the assignment int (*r)[3] = arr;
is not correct; arr
in assignments decays to a pointer to its first element, an int
, which is a different type than r
which is a pointer to an array of three ints. Now admittedly, C originally wasn't that picky, and assignments between different pointer types and between pointers and integers weren't given much thought — it's all numbers, right? But modern C tries to be more type safe, for good reason, so the proper assignment would be int (*r)[3] = &arr;
: If you want the address of an array, just take the address. Your code "works" because numerically the address of the first element is the address of the array. It's the type that's wrong, not the value.
Now to your confusion: As you noted, both point to the same address; but the object p
is pointing to is a simple int (which just so happens to be followed by two more ints in memory, together comprising arr
), while r
is pointing to the array itself.
Consequently, the type of *p
is int
while the type of *r
is int[3]
, and consequently to that sizeof *r == 3 * sizeof *p
holds.
As you know, C blurs that distinction in most contexts: For example, you could legitimately say p = *r;
, because arrays are "adjusted" or "decay" to pointers to their first element in assignments or parameter initialization.
But for sizeof
they don't. That is because indexing adds index * sizeof(element)
to the numerical value of the pointer; if the pointer points to an entire array, like r
(as opposed to its first element only, like p
), the second element will be the next array, which isn't there — there is only one array, so your program is faulty.
#include <stdio.h>
int main() {
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*r)[3] = arr;
printf("%p %p\n", (void*)r[0], (void*)r[1]);
}
This little program illustrates this. Note how arr
again decays to the address of its first element; only this time, the first element of that two-dimensional array is an array itself, of three ints, so it fits perfectly.
CodePudding user response:
The types of p
and r
are very different:
p
is a pointer toint
, initialized to point to the first element of arrayarr
,r
is a pointer to an array of 3int
: the initialization is incorrect, it should ber = &arr
.printf("%u %u", p, r)
has undefined behavior:%u
expects an argument with typeunsigned int
,p
andr
are pointers which should be cast as(void *)
and convered with%p
.printf("\n%d %d %d", p[0], p[1], p[2])
is correct and produces1 2 3
as expectedprintf("\n%d %d %d", r[0], r[1], r[2])
has undefined behavior for multiple reasons:r[0]
andr[1]
decay as pointers toint
, they should be cast as(void *)
and printed using%p
.r[2]
is an invalid pointer: computing its value and passing it as an argument has undefined behavior.- postfix unary operators bind stronger than prefix operators, so
*r[0]
as parsed as*(r[0])
, which is the same asr[0][0]
, the first element of the arrayarr
. Conversely*r[1]
is equivalent tor[1][0]
, which refers to an invalid area, beyond the end ofarr
, same for*r[2]
, so reading both of these cause undefined behavior. - conversely
(*r)[0]
is the same as(r[0])[0]
, hencer[0][0]
, the first element ofarr
, and similary(*r)[1]
is the same asr[0][1]
soprintf("\n%d %d %d", (*r)[0], (*r)[1], (*r)[2])
outputs1 2 3
just likeprintf("\n%d %d %d", p[0], p[1], p[2])
.
p
and r
(initialized as r = &arr
) indeed point to the same location, but they have different types which must be taken into consideration when writing code.
Here is a modified version:
#include <stdio.h>
int main() {
int arr[3] = { 1, 2, 3 };
int *p = arr;
int (*r)[3] = &arr;
printf("arr: address %p, sizeof(arr): %2zu bytes, sizeof(*arr): %2zu bytes\n",
(void *)arr, sizeof(arr), sizeof(*arr));
printf("p: address %p, sizeof(p): %2zu bytes, sizeof(*p): %2zu bytes\n",
(void *)p, sizeof(p), sizeof(*p));
printf("r: address %p, sizeof(r): %2zu bytes, sizeof(*r): %2zu bytes\n",
(void *)r, sizeof(r), sizeof(*r));
printf("arr: %p, arr 1: %p\n", (void *)arr, (void *)(arr 1));
printf("p: %p, p 1: %p\n", (void *)p, (void *)(p 1));
printf("r: %p, r 1: %p\n", (void *)r, (void *)(r 1));
printf("%d %d %d\n", p[0], p[1], p[2]);
printf("%d %d %d\n", r[0][0], r[0][1], r[0][2]);
printf("%d %d %d\n", (*r)[0], (*r)[1], (*r)[2]);
return 0;
}
Output:
arr: address 0x7fff544137f8, sizeof(arr): 12 bytes, sizeof(*arr): 4 bytes
p: address 0x7fff544137f8, sizeof(p): 8 bytes, sizeof(*p): 4 bytes
r: address 0x7fff544137f8, sizeof(r): 8 bytes, sizeof(*r): 12 bytes
arr: 0x7fff544137f8, arr 1: 0x7fff544137fc
p: 0x7fff544137f8, p 1: 0x7fff544137fc
r: 0x7fff544137f8, r 1: 0x7fff54413804
1 2 3
1 2 3
1 2 3
CodePudding user response:
*p is a pointer to an array, while (*r)[3] is an array pointer to array.
*p will point to the values/indices of the array arr. (*r)[i] will point to the memory location/address of the indices.
*r will point no where as (*r) and *r are DIFFERENT.