为什么解除引用运算符无法返回指向数组的"指针指向的值"变量?

时间:2017-01-31 17:50:06

标签: c pointers gcc

考虑一下这个程序,

#include <stdio.h>

int main(){
  int a[][4] = {2,3,4,5,
                43,32,76,3
               };
  int *p;
  int (*q)[4];

  p = (int *)a;
  q = a;
  printf("%u\t%u", (unsigned int)p,(unsigned int)q);
  printf("\n%d\t%d",*(++p),*(++q));
  return 0;
}

在上面的程序中,我已经定义了两个指针,

1)整数指针

2)指向数组的指针

使用GCC编译器编译时,我遇到了两个疑问,

问题1:

为什么,声明,

printf("\n%d\t%d",*(++p),*(++q));
当我尝试取消引用(++q)时,

会返回一个地址吗?

问题2:

我有什么方法可以使用&#34;指向数组的指针&#34;变量(&#39; q&#39; - 在这个程序中)来访问一行中的连续元素?

编译:GCC;操作系统:Ubuntu

3 个答案:

答案 0 :(得分:4)

  1. *(++q)不会“返回地址”。它返回类型int[4]的结果,它是数组类型的左值。该左值是指数组a[1]的{​​{1}}子数组。

    这意味着您的问题的标题根本不准确:在这种情况下,取消引用运算符确实返回它应该返回的内容。指针被声明为指向数组的指针,而解除引用运算符的计算结果为 array 类型的左值。

    然而,在此之后,您在衰减到指针类型的上下文中使用该数组左值,就像任何其他数组一样(参见What is array decaying?Why do arrays in C decay to pointers?) 。将数组作为参数传递给a恰好是这样的背景。因此,原始数组左值衰减到指针。该指针值指向printf的开头,与a[1]相同。该指针值传递给&a[1][0],您尝试使用printf打印它。

    (请注意,在%d中使用%d来打印指针值会触发未定义的行为。)

  2. 嗯,您可以以任何方式访问它们

    printf

答案 1 :(得分:1)

此声明

int (*q)[4];

声明指向int[4]类型对象的指针。为了简化理解,您可以通过以下方式引入typedef

typedef int T[4];

T *q;
q = a;

因此,取消引用指针,您将获得类型T的指向对象,该对象表示类型为int[4]的数组。结果在printf函数

中使用此对象
printf("\n%d\t%d",*(++p),*(++q));
                         ^^^^^

你将获得数组++q的第二个“行”(由于递增指针a),而这又是int[4]类型的一维数组隐式转换为指向其第一个元素的指针。

因此,表达式*(++q)的类型为int *,并指向数组a的第二个“行”的第一个元素。

如果要使用此指针遍历数组a的元素,可以按以下方式执行

#include <stdio.h>

int main(void) 
{
    int a[][4] = { { 2, 3, 4, 5 }, { 43, 32, 76, 3 } };

    int ( *q )[4] = a;

    for ( size_t i = 0; i < sizeof( a ) / sizeof( *a ); i++ )
    {
        for ( size_t j = 0; j < sizeof( *q ) / sizeof( **q ); j++ )
        {
            printf( "%2d ", q[i][j] );
        }
        printf( "\n" );
    }

    return 0;
}

程序输出

 2  3  4  5 
43 32 76  3 

q[0]是数组a的第一个“行”。您也可以只写*qq[1]是数组a的第二个“行”。您也可以像*( q + 1 )一样撰写。要访问每行的元素,您可以应用下标运算符(第一行)

q[0][i]其中i是某个索引值。或者你也可以像( *q )[i]

那样写

并且喜欢(第二行)q[1][i]( *( q + 1 ) )[i]

**q这样的特殊表达式会产生数组a第一行的第一个元素。

答案 2 :(得分:0)

TL; DR 回答,取消引用指向数组的指针会产生一个数组,该数组一旦作为函数参数传递,就会衰减到指向数组第一个元素的指针。因此,最终的是指针类型(前者和后者是指向不同类型的指针,但它们仍是指针)。

那说,首先

 printf("%u\t%u", (unsigned int)p,(unsigned int)q);

是依赖于实现的行为,因为指向整数的指针的转换是实现定义的。如果有的话,您可以将指针转换为uintptr_t类型并使用PRIuPTR格式说明符进行打印以打印该值。

其次,指针算术遵循数据类型。 q是指向4 int个数组的指针。一旦递增,它指向第一个数组。但是,在提供的格式说明符中存在不匹配。

 printf("\n%d\t%d",*(++p),*(++q));
               ^^         ^^^^^^

详细说明,q是指向数组的指针类型。取消引用会生成一个数组,现在一旦数组作为参数传递给函数,它就会衰减到指向数组第一个元素的指针。因此,基本上,您将指针作为参数传递给%d格式说明符,这是不匹配,导致undefined behavior

最后,关于对单个元素的访问,您可以使用

  • (*(q+m))[n] - &gt;首先取消引用q以获取数组,然后使用索引
  • q[m][n] - &gt; q[m]生成数组,然后使用n作为索引来获取单个元素。