以下声明的区别是什么:

int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);

理解更复杂的声明的一般规则是什么?


当前回答

以下是我的解释:

int *something[n];

优先级注意:数组下标操作符([])优先级高于 解引用运算符(*)。

因此,这里我们将在*之前应用[],使语句等价于:

int *(something[i]);

注意声明的意义:int num表示num是int, int *ptr或int (*ptr)表示,(值在ptr)是 一个int型,使PTR成为一个指向int型的指针。

这可以理解为,(值(值在第i个索引的东西))是一个整数。因此,(value在something的第i个索引处)是一个(整型指针),这使得something成为一个整型指针数组。

在第二个例子中,

int (*something)[n];

要理解这句话,你必须熟悉这个事实:

数组指针表示的注意事项:somethingElse[i]等价于*(somethingElse + i)

因此,用(*something)替换somethingElse,我们得到*(*something + i),根据声明,这是一个整数。所以,(*something)给了我们一个数组,这使得something等价于(指针指向数组)。

其他回答

typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];

以下是我的解释:

int *something[n];

优先级注意:数组下标操作符([])优先级高于 解引用运算符(*)。

因此,这里我们将在*之前应用[],使语句等价于:

int *(something[i]);

注意声明的意义:int num表示num是int, int *ptr或int (*ptr)表示,(值在ptr)是 一个int型,使PTR成为一个指向int型的指针。

这可以理解为,(值(值在第i个索引的东西))是一个整数。因此,(value在something的第i个索引处)是一个(整型指针),这使得something成为一个整型指针数组。

在第二个例子中,

int (*something)[n];

要理解这句话,你必须熟悉这个事实:

数组指针表示的注意事项:somethingElse[i]等价于*(somethingElse + i)

因此,用(*something)替换somethingElse,我们得到*(*something + i),根据声明,这是一个整数。所以,(*something)给了我们一个数组,这使得something等价于(指针指向数组)。

根据经验,右一元操作符(如[]、()等)优先于左一元操作符。因此,int *(*ptr)()[];将是指向一个函数的指针,该函数返回一个指向int的指针数组(在括号之外尽快获得正确的操作符)

最后两个的答案也可以从C中的黄金法则中扣除:

使用后声明。

int (* arr2) [8];

如果你解引用arr2会发生什么?你得到一个8个整数的数组。

int * (arr3 [8]);

如果从arr3中取一个元素会怎样?你得到一个指向整数的指针。

这也有助于处理指向函数的指针。以sigjuice为例:

Float *(*x)(void)

解引用x会发生什么?你得到一个不带参数调用的函数。当你调用它时会发生什么?它将返回一个指向浮点数的指针。

不过运算符优先级总是很棘手。然而,使用圆括号实际上也会令人困惑,因为声明紧随使用之后。至少,对我来说,直觉上arr2看起来像一个8个指向int型的指针的数组,但实际上是相反的。只是需要一些时间去适应。如果你问我,有足够的理由总是在这些声明中添加注释:)

编辑:示例

顺便说一下,我偶然发现了以下情况:一个函数有一个静态矩阵,并使用指针算术来查看行指针是否出界。例子:

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

#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))

int *
put_off(const int newrow[2])
{
    static int mymatrix[3][2];
    static int (*rowp)[2] = mymatrix;
    int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);

    memcpy(rowp, newrow, sizeof(*rowp));
    rowp += 1;
    if (rowp == border) {
        rowp = mymatrix;
    }

    return *rowp;
}

int
main(int argc, char *argv[])
{
    int i = 0;
    int row[2] = {0, 1};
    int *rout;

    for (i = 0; i &lt; 6; i++) {
        row[0] = i;
        row[1] += i;
        rout = put_off(row);
        printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
    }

    return 0;
}

输出:

0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]

Note that the value of border never changes, so the compiler can optimize that away. This is different from what you might initially want to use: const int (*border)[3]: that declares border as a pointer to an array of 3 integers that will not change value as long as the variable exists. However, that pointer may be pointed to any other such array at any time. We want that kind of behaviour for the argument, instead (because this function does not change any of those integers). Declaration follows use.

(附注:请随意改进此示例!)

按照K&R的建议,使用cdecl程序。

$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>

反之亦然。

cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )