【问题标题】:Subtle Concept of pointers and array指针和数组的微妙概念
【发布时间】:2017-06-01 08:54:18
【问题描述】:

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

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;
}

【问题讨论】:

标签: c arrays pointers dereference pointer-arithmetic


【解决方案1】:

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

嗯,你正在解引用,只是没有使用解引用运算符*

数组下标运算符[] 在这里起到解除引用的作用。引用C11,第 6.5.2.1 章

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

它是一种语法糖。表达式x[i]*(x+i) 是等价的。后者满足了您的期望,而第一个掩盖了取消引用运算符,但完成了您期望的完全相同的工作。


也就是说,还要密切关注数据类型。正如您所期望的那样,*x[i] 的内容将是完全无效的,因为它归结为类似于 `((x+i) ) 的内容。现在,

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

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

C11,第 6.5.3.2 章。


回答附加问题

让我再次引用这句话,这是第 2 段第 6.5.2.1 章的引文

连续的下标运算符指定多维数组对象的一个​​元素。 如果E 是一个n 维数组(n ≥ 2),维度为i × j × . . . × k,则E(用作 除了左值)被转换为指向(n − 1)维数组的指针 尺寸j × . . . × k。如果一元 * 运算符显式应用于此指针,或者 隐式作为下标的结果,结果是引用的(n − 1)-维 数组,如果用作左值以外的数组,它本身会转换为指针。它跟随 由此,数组以行优先顺序存储(最后一个下标变化最快)。

考虑由声明定义的数组对象

  int x[3][5];

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

【讨论】:

  • 非常抱歉,您能用更简单的话解释一下这部分吗?从 开始,x 首先被转换为指向五个整数的初始数组的指针。我迷路了..
  • @elpsyCongroo 我一定会尝试的,请给我一些时间,我现在有点忙,我会回来做一个更非正式的解释。有道理吗?
【解决方案2】:

没关系。那里没有*。

如果你有指向有效数据数组的指针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] 会给你同样的结果。

【讨论】:

    【解决方案3】:

    在 C 中,在 i[x] 处执行 x[i] 等效(是的,我知道)。你的编译器通过这样做来编译它:

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

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

    【讨论】:

    • 这是什么:是的,我知道
    • @Gaulois94 抱歉,i[x] 是什么意思?现在让我感到困惑..索引如何超出数组? i 是数组元素的索引,对吧?
    • 检查一下你的编译器做了什么。对他来说,x[i] 等价于 *(x+i),而 i[x] 等价于 *(i+x)。两者都是一样的:它需要 x 并在内存中前进 i。
    【解决方案4】:

    您误解了数组的工作原理。事实上,当使用诸如int 之类的变量时,您应该通过

    访问它的地址

    int x; int *p2x = &x

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

    【讨论】:

      【解决方案5】:

      您的问题的简单答案是:

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

      【讨论】:

      • @elpsyCongroo 肯定是真的。如果直接使用变量,则使用指向第一个元素的指针。
      • 在尝试回答更多问题之前,请阅读How do I write a good answer?
      猜你喜欢
      • 1970-01-01
      • 2014-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多