以下声明之间有什么区别:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
理解更复杂的声明的一般规则是什么?
const
和 volatile
限定词,它们既重要又棘手。
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
第三个和第一个一样。
一般规则是operator precedence。随着函数指针的出现,它会变得更加复杂。
按照 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 )
我不知道它是否有正式名称,但我称它为 Right-Left Thingy(TM)。
从变量开始,然后向右、向左、向右……以此类推。
int* arr1[8];
arr1
是一个包含 8 个整数指针的数组。
int (*arr2)[8];
arr2
是一个指向 8 个整数数组的指针(括号在左右两边)。
int *(arr3[8]);
arr3
是一个包含 8 个整数指针的数组。
这应该可以帮助您完成复杂的声明。
( ) [ ]
的从左到右的关联性和 * &
的从右到左的关联性
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
[5]
)代表内部维度。这意味着 (*a[8])
是第一个维度,因此是数组的外部表示。 a
指向 中的每个元素是一个大小为 5 的不同整数数组。
最后两个的答案也可以从 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 < 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]
请注意,边框的值永远不会改变,因此编译器可以优化它。这与您最初可能想要使用的不同:const int (*border)[3]
:将边界声明为指向 3 个整数数组的指针,只要变量存在,该数组的值就不会改变。但是,该指针可以随时指向任何其他此类数组。相反,我们希望参数具有这种行为(因为此函数不会更改任何这些整数)。声明遵循使用。
(ps:请随时改进此示例!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
根据经验,右一元运算符(如 []
、()
等)优先于左一元运算符。因此,int *(*ptr)()[];
将是一个指向函数的指针,该函数返回指向 int 的指针数组(离开括号后尽快获取正确的运算符)
error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];
和 $ gcc -std=c11 -pedantic-errors test.c
int *(*ptr)();
允许稍后使用像 p()[3]
(或 (*p)()[3]
)这样的表达式。
int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }
并这样称呼它:foo(arr)[4];
应该包含 arr[2][4]
,对吗?
我认为我们可以使用简单的规则..
example int * (*ptr)()[];
start from ptr
“ptr
是一个指向“向右移动 ..its”)”的指针,现在向左移动它的一个“(”出来向右移动“()”所以“指向一个不带参数的函数”向左移动“并返回一个指针“向右”指向一个数组“向左”整数“
)
,现在向左走……它是 *
,“指向”的指针向右走……它是 {1 },现在向左...这是一个(
出来,向右()
所以“到一个不带参数的函数”向右...[]
“并返回一个数组”向右{7 } 结束,所以向左走... *
“指向”的指针向左走... int
“整数”
以下是我的解释:
int *something[n];
优先级注意事项:数组下标运算符 ([]) 的优先级高于取消引用运算符 (*)。
因此,在这里我们将在 *
之前应用 []
,使语句等效于:
int *(something[i]);
注意声明的意义:int num 表示 num 是一个 int,int *ptr 或 int (*ptr) 表示,(value at ptr) 是一个 int,这使得 ptr 成为一个指向 int 的指针。
这可以读作,((某物的第 i 个索引处的值)的值)是一个整数。因此,(某事物的第 i 个索引处的值)是一个(整数指针),它使某事物成为整数指针数组。
在第二个中,
int (*something)[n];
为了理解这个陈述,你必须熟悉这个事实:
注意数组的指针表示:somethingElse[i] 等价于 *(somethingElse + i)
因此,将 somethingElse
替换为 (*something)
,我们得到 *(*something + i)
,根据声明它是一个整数。所以,(*something)
给了我们一个数组,它使得某些东西等价于 (指向数组的指针)。
我想第二个声明让很多人感到困惑。这是一个简单的方法来理解它。
让我们有一个整数数组,即int B[8]
。
让我们还有一个指向 B 的变量 A。现在,A 处的值是 B,即 (*A) == B
。因此 A 指向一个整数数组。在您的问题中, arr 类似于 A。
同样,在 int* (*C) [8]
中,C 是指向整数指针数组的指针。
int *arr1[5]
在此声明中,arr1
是一个由 5 个指向整数的指针组成的数组。原因:方括号的优先级高于 *(取消引用运算符)。在这种类型中,行数是固定的(这里是 5),但列数是可变的。
int (*arr2)[5]
在此声明中,arr2
是一个指向 5 个元素的整数数组的指针。原因:这里,() 括号的优先级高于 []。在这种类型中,行数是可变的,但列数是固定的(这里是 5)。
在指向整数的指针中,如果指针递增,则它进入下一个整数。
在指针数组中,如果指针递增,则跳转到下一个数组
( ) [ ]
从左到右关联并且比*
具有更高的优先级,因此将int* arr[8]
读取为大小为 8 的数组,其中每个元素指向一个 int,而int (*arr)[8]
作为指向大小为 8 的数组的指针,该数组包含整数