简体   繁体   English

`int *p` 和 `int (*p)[3]` 之间的区别?

[英]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: 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如您所见, pr包含相同的地址,然后

  • why does p[0] work but r[0] doesn't?为什么p[0]有效而r[0]无效?
  • what goes behind p[0] ? p[0]背后是什么?
  • why does (*r)[0] work but others don't?为什么(*r)[0]工作而其他人不工作?

EDIT: Other than accepted answer, this answer is also helpful https://stackoverflow.com/a/71218214/12491154编辑:除了接受的答案,这个答案也很有帮助https://stackoverflow.com/a/71218214/12491154

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 * .用作初始值设定项的数组arr被隐式转换为指向其第一个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.所以在第二个声明中初始化的 object 和初始化器有不同的指针类型并且类型之间没有隐式转换。

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.现在指针p和r都存储了相同的值:数组占用的memory范围的地址,但类型不同。

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 .那么第一次调用将int等于4的 object 类型的大小,而第二次调用将 output 等于12int[3]类型的整个数组的大小。

In this your call of printf在此您拨打 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].表达式 r[0]、r[1]、r[2] 的类型为 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.但是这arrays除了表示数组arr的数组r[0]外都不存在。 So there takes place an access to memory beyond the array arr,所以在数组 arr 之外发生了对 memory 的访问,

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

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.也不正确,因为 arrays 类型为int[3]r[1]r[2]不存在。

This call of printf这个电话printf

printf("\n%d %d %d", (*r)[0], (*r)[1], (*r)[2]);

outputs elements of the array arr due to using the expression *r .由于使用表达式*r输出数组arr的元素。

The difference between r and p is solely in the type. rp之间的区别仅在于类型。

The first thing to note is that the assignment int (*r)[3] = arr;首先要注意的是赋值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.赋值中的arr衰减为指向其第一个元素的指针,即int ,它与r不同,后者是指向三个 int 的数组的指针。 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?诚然,C 最初并没有那么挑剔,不同指针类型之间以及指针与整数之间的赋值也没有考虑太多——都是数字,对吧? But modern C tries to be more type safe, for good reason, so the proper assignment would be int (*r)[3] = &arr;但是现代的 C 试图更加类型安全,这是有充分理由的,所以正确的赋值应该是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.但是 object p指向的是一个简单的 int(恰好后面跟着 memory 中的两个整数,一起组成arr ),而r指向数组本身。

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.因此, *p的类型是int*r的类型是int[3] ,因此sizeof *r == 3 * sizeof *p成立。

As you know, C blurs that distinction in most contexts: For example, you could legitimately say p = *r;如您所知,C 在大多数情况下模糊了这种区别:例如,您可以合理地说p = *r; , because arrays are "adjusted" or "decay" to pointers to their first element in assignments or parameter initialization. ,因为 arrays 在赋值或参数初始化中被“调整”或“衰减”为指向其第一个元素的指针。

But for sizeof they don't.但是对于sizeof他们没有。 That is because indexing adds index * sizeof(element) to the numerical value of the pointer;那是因为索引将index * sizeof(element)添加到指针的数值上; 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.如果指针指向整个数组,例如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]);
}

This little program illustrates this.这个小程序说明了这一点。 Note how arr again decays to the address of its first element;请注意arr如何再次衰减到其第一个元素的地址; only this time, the first element of that two-dimensional array is an array itself, of three ints, so it fits perfectly.只是这一次,那个二维数组的第一个元素是一个数组本身,三个整数,所以它非常适合。

The types of p and r are very different: pr的类型差别很大:

  • p is a pointer to int , initialized to point to the first element of array arr , p是指向int的指针,初始化为指向数组arr的第一个元素,
  • r is a pointer to an array of 3 int : the initialization is incorrect, it should be r = &arr . r是一个指向 3 int数组的指针:初始化不正确,应该是r = &arr
  • printf("%u %u", p, r) has undefined behavior: %u expects an argument with type unsigned int , p and r are pointers which should be cast as (void *) and convered with %p . printf("%u %u", p, r)具有未定义的行为: %u期望类型为unsigned int的参数, pr是指针,应转换为(void *)并与%p转换。
  • printf("\n%d %d %d", p[0], p[1], p[2]) is correct and produces 1 2 3 as expected printf("\n%d %d %d", p[0], p[1], p[2])是正确的并按预期生成1 2 3
  • printf("\n%d %d %d", r[0], r[1], r[2]) has undefined behavior for multiple reasons: r[0] and r[1] decay as pointers to int , they should be cast as (void *) and printed using %p . printf("\n%d %d %d", r[0], r[1], r[2])由于多种原因具有未定义的行为: r[0]r[1]衰减为指向int的指针,它们应该被转换为(void *)并使用%p打印。 r[2] is an invalid pointer: computing its value and passing it as an argument has undefined behavior. r[2]是无效指针:计算其值并将其作为参数传递具有未定义的行为。
  • postfix unary operators bind stronger than prefix operators, so *r[0] as parsed as *(r[0]) , which is the same as r[0][0] , the first element of the array arr .后缀一元运算符比前缀运算符绑定更强,因此*r[0]被解析为*(r[0]) ,与数组arr的第一个元素r[0][0]相同。 Conversely *r[1] is equivalent to r[1][0] , which refers to an invalid area, beyond the end of arr , same for *r[2] , so reading both of these cause undefined behavior.相反, *r[1]等同于r[1][0] ,它指的是无效区域,超出arr的末尾,与*r[2]相同,因此读取这两个都会导致未定义的行为。
  • conversely (*r)[0] is the same as (r[0])[0] , hence r[0][0] , the first element of arr , and similary (*r)[1] is the same as r[0][1] so printf("\n%d %d %d", (*r)[0], (*r)[1], (*r)[2]) outputs 1 2 3 just like printf("\n%d %d %d", p[0], p[1], p[2]) .相反(*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 3printf("\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. pr (初始化为r = &arr )确实指向相同的位置,但它们具有不同的类型,在编写代码时必须考虑到。

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: 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

*p is a pointer to an array, while (*r)[3] is an array pointer to an array. *p是指向数组的指针,而(*r)[3]是指向数组的数组指针。

*p will point to the values/indices of the array arr whereas (*r)[i] will point to the memory location/address of the indices. *p将指向数组 arr 的值/索引,而(*r)[i]将指向索引的 memory 位置/地址。

*r will point nowhere as (*r) and *r are DIFFERENT. *r将指向任何地方,因为(*r)*r是不同的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM