'Difference between `int *p` and `int (*p)[3]`?
#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?
EDIT: Other than accepted answer, this answer is also helpful https://stackoverflow.com/a/71218214/12491154
Solution 1:[1]
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.
Solution 2:[2]
The types of p and r are very different:
pis a pointer toint, initialized to point to the first element of arrayarr,ris a pointer to an array of 3int: the initialization is incorrect, it should ber = &arr.printf("%u %u", p, r)has undefined behavior:%uexpects an argument with typeunsigned int,pandrare 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 3as 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 3just 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
Solution 3:[3]
*p is a pointer to an array, while (*r)[3] is an array pointer to an array.
*p will point to the values/indices of the array arr whereas (*r)[i] will point to the memory location/address of the indices.
*r will point nowhere as (*r) and *r are DIFFERENT.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | chqrlie |
| Solution 3 | Vishal A. |
