以下声明之间有什么区别:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
理解更复杂声明的一般规则是什么?
答案 0 :(得分:418)
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
第三个与第一个相同。
一般规则是operator precedence。随着函数指针的出现,它会变得更加复杂。
答案 1 :(得分:256)
按照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 )
答案 2 :(得分:124)
我不知道它是否有正式名称,但我称之为Right-Left Thingy(TM)。
从变量开始,然后向右,向左,向右......依此类推。
int* arr1[8];
arr1是一个包含8个整数指针的数组。
int (*arr2)[8];
arr2是指向8个整数数组的指针(括号内的左右)。
int *(arr3[8]);
arr3是一个包含8个整数指针的数组。
这可以帮助你解决复杂的声明。
答案 3 :(得分:25)
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
答案 4 :(得分:15)
最后两个答案也可以从C中的黄金法则中扣除:
声明随后使用。
int (*arr2)[8];
如果你取消引用arr2会怎么样?你得到一个8个整数的数组。
int *(arr3[8]);
如果你从arr3中取一个元素会怎么样?你得到一个指向整数的指针。
这在处理指向函数的指针时也很有用。以sigjuice为例:
float *(*x)(void )
取消引用x后会发生什么?你得到一个你可以不带参数调用的函数。你打电话后会发生什么?它将返回指向浮点数的指针。
但是,运算符优先级总是很棘手。但是,使用括号实际上也可能会造成混淆,因为声明随后使用。至少对我而言,直观地说,arr2看起来像是一个由8个指针组成的数组,但它实际上是另一种方式。只需要一些习惯。如果你问我,总是足以在这些声明中添加评论的原因:)
编辑:示例
顺便说一句,我偶然发现了以下情况:一个具有静态矩阵的函数,它使用指针算法来查看行指针是否超出范围。例如:
#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]
请注意,border的值永远不会改变,因此编译器可以对其进行优化。这与您最初可能要使用的内容不同:const int (*border)[3]
:将border声明为指向3个整数数组的指针,只要该变量存在,它就不会更改值。但是,该指针可以在任何时候指向任何其他这样的数组。我们想要参数的那种行为(因为这个函数不会改变任何整数)。声明随后使用。
(p.s:随意改进这个样本!)
答案 5 :(得分:5)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
答案 6 :(得分:3)
根据经验,正确的一元运算符(如[]
,()
等)优先于左运算符。所以,int *(*ptr)()[];
将是一个指向一个函数的指针,该函数返回一个指向int的指针数组(当你离开括号时,尽快得到正确的运算符)
答案 7 :(得分:2)
我认为我们可以使用简单的规则..
example int * (*ptr)()[];
start from ptr
“ptr
是指向”
走向正确... ..“”现在离开它的“(”
出来右边“()”所以
“到一个不带参数的函数”向左转“并返回指针”向右“
一个数组“左转”的整数“
答案 8 :(得分:2)
以下是我如何理解它:
int *something[n];
注意优先级:数组下标运算符(&#39; []&#39;)的优先级高于 解除引用运算符(&#39; *&#39;)。
所以,在这里我们将应用&#39; []&#39;在&#39; *&#39;之前,使声明等同于:
int *(something[i]);
注意声明是如何理解的:
int num
表示(num)是(int),int *ptr
或int (*ptr)
表示,(ptr处的值)是 一个(int),它使ptr成为指向int的指针。
这可以读作,((某物的第i个索引处的值))是一个整数。所以,(某事物的第i个索引处的值)是一个(整数指针),它使某事成为整数指针数组。
在第二个中,
int (*something)[n];
要理解这句话,你必须熟悉这个事实:
注意数组的指针表示:somethingElse [i]相当于*(somethingElse + i)
所以,用(* something)替换somethingElse,得到*(* something + i),这是一个声明的整数。所以,(*某事)给了我们一个数组,它使得某些东西等同于(指向数组的指针)。
答案 9 :(得分:2)
这是一个有趣的网站,解释了如何读取C中的复杂类型: http://www.unixwiz.net/techtips/reading-cdecl.html
答案 10 :(得分:0)
我猜第二个声明让许多人感到困惑。这是理解它的简单方法。
让我们有一个整数数组,即int B[8]
。
我们还有一个指向B的变量A.现在,A的值是B,即(*A) == B
。因此A指向整数数组。在你的问题中,arr类似于A.
类似地,在int* (*C) [8]
中,C是指向整数的指针数组的指针。
答案 11 :(得分:0)
int *arr1[5]
在此声明中,arr1
是5个指向整数的指针的数组。
原因:方括号的优先级高于*(取消引用的运算符)。
在这种类型中,行数是固定的(此处为5),但列数是可变的。
int (*arr2)[5]
在此声明中,arr2
是指向5个元素的整数数组的指针。
原因:()中的括号优先于[]。
在这种类型中,行数是可变的,但列数是固定的(此处为5)。
答案 12 :(得分:-7)
如果指针递增,则在指向整数的指针中,它会转到下一个整数。
在指针数组中,如果指针递增,则跳转到下一个数组