指向数组的C指针/数组指针消歧

以下声明的区别是什么:

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

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

396862 次浏览
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers

第三个和第一个一样。

一般规则是运算符优先级。当函数指针出现时,它甚至会变得更加复杂。

我不知道它是否有一个正式的名字,但我称之为左右事物(TM)。

从变量开始,然后向右、向左、向右……等等。

int* arr1[8];

arr1是一个包含8个指向整数的指针的数组。

int (*arr2)[8];

arr2是一个指向8个整数数组的指针(括号块左右)。

int *(arr3[8]);

arr3是一个包含8个指向整数的指针的数组。

这将帮助您处理复杂的声明。

根据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 )

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

使用后声明。

int (*arr2)[8];

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

int *(arr3[8]);

如果你从arr3中获取一个元素会发生什么?你得到一个指向整数的指针。

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

float *(*x)(void )

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

不过运算符优先级总是很棘手。然而,使用圆括号实际上也会令人困惑,因为声明紧随使用之后。至少,对我来说,直觉上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]

注意,border的值永远不会改变,所以编译器可以优化它。这与你最初可能想使用的const int (*border)[3]:不同,const int (*border)[3]:声明border为一个指向3个整数数组的指针,只要变量存在,该数组的值就不会改变。但是,该指针可以在任何时候指向任何其他这样的数组。相反,我们希望参数也有这种行为(因为这个函数不会改变任何整数)。使用后声明。

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

typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
int *a[4]; // Array of 4 pointers to int


int (*a)[4]; //a is a pointer to an integer array of size 4


int (*a[8])[5]; //a is an array of pointers to integer array of size 5

我认为我们可以使用简单的规则。

example int * (*ptr)()[];
start from ptr
" ptr是指向"的指针" 向右走。它是")"现在向左,它是"(" 出来向右走”()“所以 指向一个不接受参数" go left "并返回一个" go right "指向的指针的函数

在指向整数的指针中,如果指针是递增的,那么它将移动到下一个整数。

在指针数组中,如果指针增加,则跳转到下一个数组

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

以下是我的解释:

int *something[n];

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

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

int *(something[i]);
注意声明的意义:int num表示__ABC1是intint *ptrint (*ptr)表示,(值为ptr)为 int,使ptr成为指向int的指针

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

在第二个例子中,

int (*something)[n];

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

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

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

我想第二个宣言让很多人感到困惑。这里有一个简单的理解方法。

假设有一个整数数组,即int B[8]

让我们还有一个指向B的变量a。现在,a处的值是B,即(*A) == B。因此A指向一个整数数组。在你的问题中,arr类似于A。

类似地,在int* (*C) [8]中,C是一个指向整数指针数组的指针。

这里有一个有趣的网站,解释了如何在C语言中读取复杂类型: http://www.unixwiz.net/techtips/reading-cdecl.html < / p >
int *arr1[5]
在这个声明中,arr1是一个包含5个指向整数的指针的数组。 原因:方括号的优先级高于*(取消引用操作符)。 在这种类型中,行数是固定的(这里是5),但列数是可变的
int (*arr2)[5]
在这个声明中,arr2是一个指向5个元素的整数数组的指针。 原因:这里,()括号的优先级高于[]。 在这种类型中,行数是可变的,但列数是固定的(这里是5).