指针和数组的微妙概念

Subtle Concept of pointers and array

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

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

没关系。那里没有*。

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

在 C 语言中,执行 x[i] 相当于(是的,我知道)在 i[x]。您的编译器通过执行以下操作来编译它:

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

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

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

访问它的地址

int x; int *p2x = &x

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

您问题的简单答案是:

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

don't understand why it no longer requires dereferencing to obtain the array (element)

好吧,你 取消引用,只是没有使用取消引用运算符 *.

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

A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

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


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

  • xint [] 类型(整数数组,衰减为指向整数的指针)
  • x+i 为您提供指向 int 类型结果的指针。
  • 内部取消引用运算符应用于它,导致 int
  • 外层*,现在会尝试对int类型的操作数(不是指针)进行操作,而且,这个操作将是一个语法错误,因为,取消引用的约束接线员说,

The operand of the unary * operator shall have pointer type.

C11,章节 §6.5.3.2.


回答附加问题

让我再次引用这句话,这句话来自第 6.5.2.1 章第 2 段

Successive subscript operators designate an element of a multidimensional array object. If E is an n-dimensional array (n ≥ 2) with dimensions i × j × . . . × k, then E (used as other than an lvalue) is converted to a pointer to an (n − 1)-dimensional array with dimensions j × . . . × k. If the unary * operator is applied to this pointer explicitly, or implicitly as a result of subscripting, the result is the referenced (n − 1)-dimensional array, which itself is converted into a pointer if used as other than an lvalue. It follows from this that arrays are stored in row-major order (last subscript varies fastest).

Consider the array object defined by the declaration

  int x[3][5];

Here x is a 3 × 5 array of ints; more precisely, x is an array of three element objects, each of which is an array of five ints. In the expression x[i], which is equivalent to (*((x)+(i))), x is first converted to a pointer to the initial array of five ints. Then i is adjusted according to the type of x, which conceptually entails multiplying i by the size of the object to which the pointer points, namely an array of five int objects. The results are added and indirection is applied to yield an array of five ints. When used in the expression x[i][j], that array is in turn converted to a pointer to the first of the ints, so x[i][j] yields an int.