【问题标题】:How does de-referencing work for pointer to an array?取消引用对指向数组的指针有何作用?
【发布时间】:2019-10-10 08:08:12
【问题描述】:

取消引用时指向元素数组的指针返回地址。 由于它保存了数组第一个元素的地址,因此取消引用它应该返回一个值。

int arr[] = { 3, 5, 6, 7, 9 }; 
int *p = arr; 
int (*ptr)[5] = &arr;     
printf("p = %p, ptr = %p\n", p, ptr); 
printf("*p = %d, *ptr = %p\n", *p, *ptr);

输出:

p = 0x7fff6ea72d10, ptr = 0x7fff6ea72d10

*p = 3, *ptr = 0x7fff6ea72d10

为什么*ptr返回数组的基地址,不应该返回那个地址的值吗??

【问题讨论】:

    标签: c pointers


    【解决方案1】:

    为什么 *ptr 返回数组的基地址,不应该 返回那个地址的值??

    (p3) 除非它是sizeof 运算符、_Alignof 运算符或一元'&' 运算符的操作数,或者是用于初始化数组的字符串文字 ,类型为 "array of type" 的表达式被转换为类型为 "pointer to type" 的表达式,它指向数组对象的初始元素,而不是一个左值。 C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)

    int (*ptr)[5] = &arr;
    

    ptrint [5]指向数组的指针。当您取消引用 ptr 时,您会得到 array of int[5]。如何访问int[5] 的数组?

    规则6.3.2.1 提供了答案:

    "array of type" 被转换为一个类型为 "pointer to type" 的表达式,它指向数组对象的初始元素...

    现在,如果您再次取消引用(例如**ptr),那么您将获得第一个元素的值。

    【讨论】:

    • 因为 ptr 存储数组的第一个元素的地址(如输出中所示)不应该取消引用它返回该地址的值?编译器怎么知道不返回值而是返回地址??
    • 这是 type 的不同,而不是 value。 ptr 是一个指向数组的指针。当您取消引用ptr(例如*ptr)时,您将拥有数组类型int[5]两个都保存第一个元素的地址,但第一个是pointer-to-array类型,第二个是类型int[5] 的数组。因此,通过间接级别,您可以作为不同类型访问对象,但只要仍然存在间接级别,您就不会访问该值。
    • @Akash 编译器可以看到目标类型是数组,因此放弃获取目标。不这样做就太愚蠢了。获取一个非常大的数组只是为了删除它,返回您从中获取的地址将是非常浪费的。
    • 这样想,ptr 持有第一个int array[5] 的地址。那么问题来了int array[5]是什么? int array[5] 访问时是第一个元素的地址——所以两者都持有相同的地址,即第一个元素的地址(但它们是完全不同的类型)。
    【解决方案2】:

    问题是

    “为什么*ptr返回数组的基地址,不应该返回那个地址的值吗?”

    确实返回该地址的值,即数组arr

    想想一队人:你可以指着第一个人说“那边的那个人”,也可以指着同一个人说“那边的那个人”。在同一个位置有 2 个东西:一个人和一个队列。数组也会发生同样的事情:person * 代表“那边的人”,person (*)[42]“代表 42 人的队列”。如果取消引用指向队列的指针,则会得到一个队列。如果您从队列中选择第一个,您会得到一个人。


    但是 那么当数组本身作为printf 的参数给出时,数组本身将衰减到addressfirst 元素。所以这里,

    int arr[] = { 3, 5, 6, 7, 9 }; 
    int (*ptr)[5] = &arr;     
    
    // undefined behaviour really, all pointers should be cast to void *
    printf("%p %p %p %p", *ptr, &ptr[0], arr, &arr[0]);
    

    这4个表达式都会产生指向int的指针,其值为数组中第一个元素的地址(arr中3的地址)。

    【讨论】:

      【解决方案3】:

      这是一个简单的、技术含量较低的解释。

      你如何设置p= arr;

      你如何设置ptr= &arr;

      您的问题确实与数组无关。 arr 可以是任何类型,答案是一样的:& 得到arr 的地址,所以无论arr 是什么,ptr 都存储它的地址,而p 存储arr自己。

      如果您执行*ptr,您将取消引用该地址,从而获得等于p 的值。

      int arr = 3;
      // clearly these are different!
      int p = arr;
      int* ptr = &arr;
      

      同样

      int x = 3;
      int* arr = &x;
      // clearly these are different!
      int* p = arr;
      int** ptr = &arr;
      // so of course they dereference differently
      printf("*p = %d, *ptr = %p\n", *p, *ptr);
      

      【讨论】:

        【解决方案4】:

        您看到的行为是“c”将arr 解释为指向第一个元素的内存位置的指针的结果。指向数组&arr 的指针将是数组本身在内存中的地址,这也是它的第一个元素的地址。结果arr&arr 产生相同的值。试试这个:

        printf("array = %p, &array = %p\n", arr, &arr);
        

        认为值相同,类型不同。 arr 是变量,&arr 是否是指向变量的指针。

        ptr 反映了同样的情况。它的值ptr = &arr 使它成为一个指向数组的指针。它包含数组的地址。 *ptr 返回数组本身,在“c”解释中是数组第一个元素的地址。因此,ptr*ptr 的值与 &arrarr 的值相同。

        希望它让它更清晰(而不是更模糊):-)

        【讨论】:

          【解决方案5】:

          数组是指针。 数组不是指针,见下面的 cmets

          指向数组的指针是一个指针,指向一个指针。尝试取消引用两次,这应该会产生一个值。

          【讨论】:

          • "数组是指针。"不,他们不是。在许多情况下,它们只是衰减为指针。
          • 区别是什么?
          • 数组不是指针而是容器,你不会惊讶于 sizeof(arr) != sizeof(p) (p is a pointer) is'是吗?只是在某些情况下,数组被用作指针,例如在函数的参数中给出
          • 一个重要的区别是大小:sizeof(T[N]) 等于 N * sizeof(T)sizeof(T*) 将等于任何其他指针的大小。然后,您不能重新分配数组,但可以重新分配指针。还有一个数组数组在内存中是连续的,但是一个指针数组会包含指向其他地方的指针。
          • @sepp2k 除了指向不同类型的指针不必大小相同。
          猜你喜欢
          • 2012-03-25
          • 2011-10-20
          • 2019-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-01
          • 2021-03-27
          • 1970-01-01
          相关资源
          最近更新 更多