C pointer to array/array of pointers disambiguation
以下声明之间的区别是什么?
1 2 3 | int* arr1[8]; int (*arr2)[8]; int *(arr3[8]); |
理解更复杂声明的一般规则是什么?
1 2 | int* arr[8]; // An array of int pointers. int (*arr)[8]; // A pointer to an array of integers |
第三个和第一个一样。
一般规则是运算符优先级。当函数指针出现在图片中时,它会变得更加复杂。
按照K&R的建议,使用CDECL程序。
1 2 3 4 5 6 7 8 9 | $ 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> |
它的工作方式也是相反的。
1 2 | cdecl> declare x as pointer to function(void) returning pointer to float float *(*x)(void ) |
我不知道它是否有正式的名字,但我称它为左右thingy(tm)。
从变量开始,然后向右,向左,向右…等等。
1 | int* arr1[8]; |
arr1是指向整数的8个指针的数组。
1 | int (*arr2)[8]; |
arr2是一个指向8个整数数组的指针(括号阻塞了左右)。
1 | int *(arr3[8]); |
arr3是指向整数的8个指针的数组。
这可以帮助您处理复杂的声明。
1 2 3 4 5 | 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 |
最后两个问题的答案也可以从C中的黄金法则中扣除:
Declaration follows use.
如果你不引用arr2怎么办?你得到一个8个整数的数组。
如果你从arr3中取一个元素会发生什么?你得到一个指向整数的指针。
这也有助于处理指向函数的指针。以Sigjuice为例:
当你取消引用x时会发生什么?您得到了一个可以不带参数调用的函数。当你叫它时会发生什么?它将返回一个指向浮点的指针。
不过,运算符优先级总是很棘手的。但是,使用括号实际上也会令人困惑,因为声明后面跟着使用。至少在我看来,arr2看起来像一个8个指向int的指针数组,但实际上却是相反的。只是需要一些习惯。如果你问我的话,有足够的理由总是在这些声明中添加注释:)
编辑:实例
顺便说一下,我刚刚偶然发现了以下情况:一个具有静态矩阵的函数,它使用指针算法来查看行指针是否越界。例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #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] ", i, (void *) rout, rout[0], rout[1]); } return 0; } |
输出:
1 2 3 4 5 6 | 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的值永远不会改变,所以编译器可以把它优化掉。这与您最初可能想要使用的不同:
(P.S.:请随时改进此样品!)
1 2 | typedef int (*PointerToIntArray)[]; typedef int *ArrayOfIntPointers[]; |
根据经验,右一元运算符(如
下面是一个有趣的网站,它解释了如何阅读C语言中的复杂类型:网址:http://www.unixwiz.net/techttips/reading-cdecl.html
以下是我的解释方法:
1 | int *something[n]; |
note on precedence: array subscript operator ('[ ]') has higher priority than
dereference operator ('*').
因此,这里我们将把"*"前面的"[]"应用于,使语句等效于:
1 | int *(something[i]); |
note on how a declaration makes sense:
int num means (num) is an (int),int *ptr orint (*ptr) means, (value at ptr) is
an (int), which makes ptr a pointer to int.
这可以理解为,((某物的第i个索引处的值)是一个整数。所以,(某物的第i个索引处的值)是一个(整型指针),它使某物成为整型指针数组。
在第二个阶段,
1 | int (*something)[n]; |
要理解这一说法,您必须熟悉这一事实:
note on pointer representation of array: somethingElse[i] is equivalent to *(somethingElse + i)
所以,用(*something)替换somethingle,我们得到*(*something+i),根据声明,它是一个整数。所以,(*某物)给了我们一个数组,它使得某物等价于(指向数组的指针)。
我想我们可以用简单的规则……
1 2 | example int * (*ptr)()[]; start from ptr |
"
我想第二个声明对很多人来说都很困惑。这是一个很容易理解的方法。
让我们有一个整数数组,即
我们还有一个变量a指向b,现在a的值是b,即
同样,在
在指向整数的指针中,如果指针递增,则转到下一个整数。
在指针数组中,如果指针递增,则跳到下一个数组