#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]);
}输出:
1483745184 1483745184
1 2 3
1483745184 1483745196 1483745208
1 0 -647513344
1 2 3如您所见,p和r包含相同的地址。
为什么p[0]
r[0]不工作?
(*r)[0]工作,其他人不工作?编辑:除了接受的答案,这个答案也是有帮助的https://stackoverflow.com/a/71218214/12491154。
发布于 2022-02-22 08:59:44
这两种声明之间的区别
int *p = arr;
int (*r)[3] = arr;在第二个声明中,使用了错误的初始化器。用作初始化器的数组arr被隐式转换为指向其类型int *的第一个元素的指针。因此,在第二个声明中,初始化对象和初始化器有不同的指针类型,并且类型之间没有隐式转换。
要使第二个声明正确,您需要编写
int (*r)[3] = &arr;现在,指针p和r存储相同的值:数组占用内存范围的地址,但类型不同。
例如,如果您要编写
printf( "sizeof( *p ) = %zu\n", sizeof( *p ) );
printf( "sizeof( *r ) = %zu\n", sizeof( *r ) );然后,第一个调用将输出int类型的对象的大小,该对象的大小等于4,而第二个调用将输出等于12的类型int[3]的整个数组的大小。
在这里,你的呼唤,打印
printf("%u %u", p, r);指针使用了不正确的转换说明符。相反,你必须写
printf("%p %p", ( void * )p, ( void * )r);表达式r、r1、r2的类型为int3。在此调用中使用
printf("\n%d %d %d", r[0], r[1], r[2]);如前所述,它们被隐式转换为指向其第一个元素的指针。但是,除了表示数组arr的数组r外,这些数组不存在。所以在数组arr之外进行内存访问,
你可以写
printf( "\n%p\n", ( void * )r[0] );这个电话就相当于
printf("\n%p\n", ( void * )arr );这是印刷品的呼唤
printf("\n%d %d %d", *r[0], *r[1], *r[2]);也不正确,因为不存在类型为r[1]和r[2]的数组int[3]。
这是印刷品的呼唤
printf("\n%d %d %d", (*r)[0], (*r)[1], (*r)[2]);由于使用表达式arr,因此输出数组*r的元素。
发布于 2022-02-22 08:39:54
r和p之间的区别仅仅在于类型。
首先要注意的是,赋值int (*r)[3] = arr;是不正确的;赋值中的arr衰变到指向其第一个元素int的指针,这是一个与r不同的类型,后者是一个指向由三个int组成的数组的指针。诚然,C最初并不是那么挑剔,不同指针类型之间以及指针和整数之间的赋值并没有得到太多的考虑--都是数字,对吗?但是现代C为了更好的理由尝试更安全的类型,所以正确的分配应该是int (*r)[3] = &arr;:如果您想要数组的地址,只需取地址即可。您的代码“工作”,因为在数字上,第一个元素的地址是数组的地址。这是错误的类型,而不是价值。
现在让您感到困惑:正如您所注意到的,两者都指向相同的地址;但是p所指向的对象是一个简单的int (恰好后面是内存中的另外两个int,它们一起包含arr),而r则指向数组本身。
因此,*p的类型是int,而*r的类型是int[3],因此sizeof *r == 3 * sizeof *p保持不变。
如您所知,C在大多数情况下模糊了这种区别:例如,您可以合法地说是p = *r;,因为数组在赋值或参数初始化中对指向其第一个元素的指针进行了“调整”或“衰减”。
但对于sizeof,它们没有。这是因为索引会将index * sizeof(element)添加到指针的数值中;如果指针指向整个数组,比如r (而不是它的第一个元素,比如p),那么第二个元素将是下一个数组,它不存在--只有一个数组,所以程序出错了。
#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]);
}这个小程序说明了这一点。注意arr是如何再次衰变到其第一个元素的地址的;只是这一次,该二维数组的第一个元素是一个数组本身,由三个it组成,因此它非常适合。
发布于 2022-02-22 09:45:16
p和r的类型非常不同:
p是指向int的指针,初始化为指向数组arr的第一个元素,r是指向3int数组的指针:初始化不正确,应该是r = &arr.printf("%u %u", p, r)有未定义的行为:%u希望使用unsigned int类型的参数,%p.printf("\n%d %d %d", p[0], p[1], p[2])和r是指针,应该转换为(void *),并与r转换为正确的指针,并产生1 2 3,因为p具有未定义的行为:r[0]和r[1]衰变为指向int的指针,它们应该转换为(void *),并使用%p打印。behavior.r[2]一元运算符比前缀运算符绑定更强,因此解析为*(r[0])的*r[0]与数组arr的第一个元素r[0][0]相同。相反,behavior.r[1][0]等价,后者指的是无效区域,在arr结束后,*r[2]也是如此,因此,读取这两个原因导致的未定义的*r[1] (*r)[0]与(r[0])[0]相同,因此r[0][0]是arr的第一个元素,而相似的(*r)[1]与r[0][1]所以printf("\n%d %d %d", (*r)[0], (*r)[1], (*r)[2])输出1 2 3的情况相同,就像printf("\n%d %d %d", p[0], p[1], p[2]).一样
p和r (初始化为r = &arr)确实指向相同的位置,但它们有不同的类型,在编写代码时必须加以考虑。
以下是修改后的版本:
#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;
}输出:
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 3https://stackoverflow.com/questions/71217293
复制相似问题