【问题标题】:what's *p when p is declared as int (*)[size] and assigned to an array当 p 声明为 int (*)[size] 并分配给数组时,*p 是什么
【发布时间】:2017-11-21 03:23:17
【问题描述】:

代码:

int arr[5] = {1,2,3,4,5};
int (*p)[5] = &arr;
printf("p:%p\n",p);
printf("*p:%p\n",*p);

结果:p = *p = arr = 0x7ffee517c830都是数组的地址

使用p访问arr[i]的正确方式是*(*p+i)

指针p的类型是int(*)[5],所以p指向一个类型为int [5]的数组。但是我们不能说p指向arr的一个不可见的shell,p毕竟是一个变量。它存储了arr的地址,也就是arr[0]的地址,arr的第一个元素。

我以为*p 会得到我1,这是arr 的第一个元素。

解引用操作是指将p中的值作为地址,从该地址获取值。对? 所以p存储了arr的地址,这里就是0x7ffee517c8301就存储在这个地址中。 **p 不是非法的吗?第一次取消引用给我们1,第二次取消引用将使用1 作为地址,这是非法的。

我错过了什么?

【问题讨论】:

  • 简单地说:int* p = arr;
  • @selbie 好吧,它们做不同的事情。 int (*p)[5]有一些用例

标签: c arrays pointers dereference


【解决方案1】:

*p 的结果是一个数组类型的左值表达式。使用(*p)与在您现在能想到的任何表达式中使用arr 完全相同

例如:

  • &*p 表示 &arr
  • **p 表示*arr(这是合法的)。
  • (*p)[i] 表示arr[i]
  • sizeof *p 表示sizeof arr

数组在这方面并不特殊。您可以使用int x; int *q = &x; 看到相同的现象。现在*qx 的含义完全一样。


关于您的最后一段,我认为您将指针想象为美化的整数而使自己感到困惑。有些人以这种方式教授指针,但 IMO 这不是一种好的教学技术,因为它会导致您现在遇到的确切困惑。

如果您取消引用 int(*)[5],您会得到一个 int[5],仅此而已。数据类型在取消引用中很重要。谈论“取消引用0x7ffee517c830”是没有意义的。同样,这不是数组所特有的。如果你取消引用 char ***,你会得到一个 char ** 等等。

在此讨论中,数组“不同”的唯一方式是,如果您尝试对它们进行算术运算或输出它们等会发生什么。例如,如果您提供 int[5] 作为 printf 参数,则有隐式转换为指向这 5 个整数中的第一个的 int *。将* 运算符应用于int[5] 时也会发生这种转换,这就是为什么您会从中得到int

【讨论】:

  • char*** 肯定指向了 char** 以外的其他地方,因为后者不能指向自身,IMO OP 可能会反对。
  • @iBug 这并不是真正的实质性区别。但是无论哪种方式,OP都需要了解我在最后一段中描述的数组到指针的隐式转换(“衰减规则”)
  • 您的观点就像我们翻译的101110101....01intfloat 会得到不同的结果。但首先我们需要知道二进制数,然后我们才能讨论如何使用它们。变量有两种含义,地址(左值)和该地址中的值(右值),但它们使用相同的符号。编译器将决定它的含义。 X(左值)=Y(右值)。同意?你的意思是*p 得到数组类型的左值表达式,因为它的类型是int [5]?那么为什么*pi得到右值如果pi指向int,而*pi类型是int
  • 我只想知道*p 得到什么二进制数,为什么会这样
  • @MMMMMCCLXXVII 您完全错误地使用了术语“左值”和“右值”。数组的对象表示是其元素连接的表示(例如1011010010111010 可能是shortchar[2] 的表示)。 *p 一个左值,它不是一个地址。没有“获取左值”或“获取右值”或“获取二进制数”之类的东西。 *pi 也是一个左值。 * 的结果始终是左值。
【解决方案2】:

p 被声明为“指向 int[5] 的指针”。

arr 被声明为 'int[5]`

所以初始化器p = &arr; 并不是那么奇怪。如果你用任何原始类型替换int[5],你不会眨眼。

*p 是 arr 的另一个句柄。所以 (*p)[0] = 1.

这真的只出现在奇怪的情况下。使用下标运算符取消引用指向数组的指针是最自然的。这是一个人为的示例,我想将表格作为参数传递。

#include <stdio.h>

int print_row_range(int (*tab) [2], int first, int last)
{
  int i;
  for(i=first; i<= last; i++)
  {
    printf("{%d, %d}\n", tab[i][0], tab[i][1]);
  }
}

int main(int argc, char *argv[])
{
  int arr[3][2] = {{1,2},{3,4},{5,6}};
  print_row_range(arr,1,2);
}

此示例将表视为行数组。

【讨论】:

    【解决方案3】:

    取消引用不会为您提供价值。它为您提供了一个对象,如果可以将其转换为,则可以将其用作其类型的值。

    *p,与arr 相同,是一个由5 个整数组成的数组的对象,所以如果你想从数组中获取一个整数,你必须取消引用它再次喜欢(*p)[3]

    考虑一个更大的例子:

    int arr[5][5];
    int (*p)[5] = arr;
    

    现在你得到 arr[0]*p,它本身是一个 5 的数组。这里有区别:

    *( p+1) == arr[1];
    *(*p+1) == arr[0][1];
      ^              ^^^
    

    明白了吗?

    【讨论】:

      【解决方案4】:

      一个用例是能够用 malloc 分配一个二维(或更多)数组指针,只有一个 malloc:

      #include <stdio.h>
      #include <stdlib.h>
      
      static int (*foo(size_t n))[42] {
        return malloc(sizeof *foo(0) * n);
        // return malloc(sizeof(int [n][42]); works too
      }
      
      int main(void) {
        size_t n = 42;
        int (*p)[42] = foo(n);
        if (!p) {
          return 1;
        }
      
        printf("p:");
        int accu = 0;
        for (size_t i = 0; i < n; i++) {
          for (size_t j = 0; j < sizeof *p / sizeof **p; j++) {
            p[i][j] = accu++;
            printf(" %d", p[i][j]);
          }
        }
        printf("\n");
        free(p);
      }
      

      我觉得这很有趣。

      VLA 的更多内容:

      #include <stdio.h>
      #include <stdlib.h>
      
      static void *foo(size_t elem, size_t n, size_t m) {
        return malloc(elem * n * m);
      }
      
      int main(void) {
        size_t n = 42;
        int (*p)[n] = foo(sizeof **p, n, n);
        if (!p) {
          return 1;
        }
      
        printf("p:");
        int accu = 0;
        for (size_t i = 0; i < n; i++) {
          for (size_t j = 0; j < sizeof *p / sizeof **p; j++) {
            p[i][j] = accu++;
            printf(" %d", p[i][j]);
          }
        }
        printf("\n");
        free(p);
      }
      

      【讨论】:

        猜你喜欢
        • 2020-05-14
        • 1970-01-01
        • 2019-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-03
        相关资源
        最近更新 更多