指针和数组的微妙概念

时间:2017-06-01 08:54:18

标签: c arrays pointers dereference pointer-arithmetic

我有一个非常奇怪的指针示例,需要您的帮助。通常,指针用于指向变量(请参阅下面的第一个示例),但指向数组时。我不明白为什么它不再需要引用来获取数组(参见下面的第二个例子)

printf("TEST: %i\n", x[i]);// I expect this should be *x[i]

这确实很奇怪。它只是一个C约定或你如何解释这个?

编辑::由于很多人提供了明确的答案,我想提出另一个小的跟进问题,因为你们所有人都提到了x [i] = *(x + i),怎么样? x [i] [j]对于二维数组?它等同于什么?是否需要取消引用?

使用正常变量

int j = 4;
int* pointerj=&j;//pointerj holds address of j or points to j
printf("%d",*pointerj); returns the value that pointer j points to 

使用数组:

 #include <stdio.h>
 #include <stdlib.h>

int *function(unsigned int tags) {
    int i;
    int *var;

    var = (int*)malloc(sizeof(int)*tags); // malloc is casted to int* type , allocated dynamical memory, it returns a pointer!
    //so now var holds the address to a dynamical array.

    for (i = 0; i < tags; i++) {
        var[i] = i;
    }

    return var;
}

int main() {
    int *x;
    int i;

    x = function(10);
    for (i = 0; i < 10; i++) {
        printf("TEST: %i\n", x[i]);// I expect this should be *x[i]
    }

    free(x); x = NULL;

    return 0;
}

5 个答案:

答案 0 :(得分:3)

  

不明白为什么它不再需要取消引用来获取数组(元素)

嗯,你 解除引用,它只是没有使用解除引用运算符*

数组下标运算符[]在此处作为解除引用的工作。引用C11,章节§6.5.2.1

  

后缀表达式后跟方括号[]中的表达式是下标   指定数组对象的元素。下标运算符[]的定义   是E1[E2](*((E1)+(E2)))相同。由于转换规则   应用于二进制+运算符,如果E1是一个数组对象(等效地,指向   数组对象的初始元素)和E2是一个整数,E1[E2]表示E2 - th   E1的元素(从零开始计算)。

这是一种语法糖。表达式x[i]*(x+i)是等效的。后者满足了你的期望,而第一个,伪装了解除引用操作符,但完成了你预期的完全相同的工作。

那就是说,也要密切关注数据类型。你所期待的,*x[i]行的某些东西将是无效的,因为它归结为类似`(x + i))的东西。现在,

  • x的类型为int [](整数数组,它衰减为指向整数的指针)
  • x+i为您提供指向int类型结果的指针。
  • 内部解除引用运算符应用于它,产生int
  • 外部*,现在将尝试对类型int(不是指针)的操作数进行操作,并且此操作将是语法错误,因为解除引用运算符的约束说,
  

一元*运算符的操作数应具有指针类型。

C11中,章节§6.5.3.2。

回答其他问题

让我再次使用这句话,第2段,第6.5.2.1章

  

连续的下标运算符指定多维数组对象的元素。   如果E是尺寸为n的{​​{1}}维数组(n ≥ 2),则为i × j × . . . × k(用作   除了左值之外)转换为指向E - 维数组的指针   尺寸(n − 1)。如果一元j × . . . × k运算符明确地应用于此指针,或   隐式地,作为下标的结果,结果是引用的* - 维度   数组,如果用作左值而不是lvalue,它本身会被转换为指针。它跟随   从这个角度来看,数组以行主要顺序存储(最后一个下标变化最快)。

     

考虑声明

定义的数组对象
(n − 1)
     

此处 int x[3][5]; x的{​​{1}}数组;更准确地说,3 × 5是一个由三个元素对象组成的数组,每个对象都是一个   五个整数阵列。在表达式int中,相当于xx[i]首先转换为   指向五个整数的初始数组的指针。然后根据概念上(*((x)+(i)))的类型调整x   需要将i乘以指针所指向的对象的大小,即五个x的数组   对象。添加结果并应用间接以产生五个整数的数组。在使用时   表达式i,该数组又转换为指向第一个整数的指针,因此int   产生x[i][j]

答案 1 :(得分:1)

这没关系。不*那里。

如果指针template <typename T> class abc{ void method(T i) { if(i is a string) do sth else if(i is an integer) do sth else else if(i is a double) do sth else else } } 指向有效数据数组,例如:

int *a

使用int *a; int arr[5] = {1, 2, 3, 4, 5]; a = arr; 您已取消引用指针,a[0]不需要*,因为a[0]*(a + 0)相同。

您可以进一步为读者提供更复杂的代码。

您可以在示例中使用i[x]而不是x[i]。为什么? x[i]等于*(x + i)但它也等同于*(i + x),这是C中的数组。

即使使用数字,例如x[3]3[x]也可以获得相同的结果。

答案 2 :(得分:1)

在C中,在[x]处做x [i]是等价的(是的,我知道)。您的编译器通过执行以下操作来编译它:

*(x + i),您可以在其中检索自己喜欢的符号。

x + i是一个指针,你从x开始并由i前进。结果考虑了指针的类型(0xff与char *的地址不同于int *的地址)。

答案 3 :(得分:1)

你误解了数组是如何工作的。实际上,当使用int之类的变量时,您应该通过

访问它的地址

int x; int *p2x = &x

但是在使用数组时,它有点不同。 int y[SOME_SIZE];是内存中的一大块字节,*y将带您到该内存位置的第一个元素,并取消引用它。 y[0]也会这样做。 *y[0]将首先获取y[0]中的值,然后尝试取消引用它...而不是您通常想要的内容:(

答案 4 :(得分:1)

您问题的简单答案是:

数组的名称是指向数组第一个元素的指针。