C 中的声明以表达式为中心,这意味着声明的形式应与可执行代码中的表达式形式相匹配。
例如,假设我们有一个指向名为p 的整数的指针。我们要访问p指向的整数值,所以我们取消引用指针,如下所示:
x = *p;
表达式的类型*p是int;因此,p 的声明采用以下形式
int *p;
在此声明中,int 是类型说明符,*p 是声明符。声明器引入了被声明对象的名称 (p),以及类型说明符未提供的其他类型信息。在这种情况下,附加的类型信息是p是一个指针类型。声明可以读作“p 是指向int 的类型指针”或“p 是指向int 类型的指针”。我更喜欢使用第二种形式,其他人更喜欢第一种。
您可以将声明编写为int *p; 或int* p;,这是C 和C++ 语法的一个意外。在这两种情况下,它都被解析为int (*p);——换句话说,* 总是与变量名相关联,而不是类型说明符。
现在假设我们有一个指向int 的指针数组,我们想要访问数组中第 i 个元素所指向的值。我们对数组进行下标并取消引用结果,如下所示:
x = *ap[i]; // parsed as *(ap[i]), since subscript has higher precedence
// than dereference.
同样,表达式*ap[i]的类型是int,所以ap的声明是
int *ap[N];
其中声明符*ap[N] 表示ap 是指向int 的指针数组。
为了说明问题,现在假设我们有一个指向int 的指针,并且想要访问该值。同样,我们尊重指针,然后我们取消引用该结果以获得整数值:
x = **pp; // *pp deferences pp, then **pp dereferences the result of *pp
由于表达式**pp的类型是int,所以声明是
int **pp;
声明符**pp 表示pp 是指向另一个指向int 的指针。
双重间接出现很多,通常是当您想要修改传递给函数的指针值时,例如:
void openAndInit(FILE **p)
{
*p = fopen("AFile.txt", "r");
// do other stuff
}
int main(void)
{
FILE *f = NULL;
...
openAndInit(&f);
...
}
在这种情况下,我们希望函数更新f 的值;为此,我们必须传递一个指向f 的指针。由于f 已经是指针类型(FILE *),这意味着我们将指针传递给FILE *,因此p 的声明为FILE **p。请记住,openAndInit 中的 表达式 *p 与 main 中的表达式 f 所指的对象相同。
在声明和表达式中,[] 和 () 的优先级都高于一元 *。例如*ap[i]被解释为*(ap[i]);表达式ap[i] 是一个指针类型,* 取消引用该指针。因此ap 是一个指针数组。如果你想声明一个指向数组的指针,你必须明确地将* 与数组名分组,如下所示:
int (*pa)[N]; // pa is a pointer to an N-element array of int
当你想访问数组中的一个值时,你必须在应用下标之前遵从pa:
x = (*pa)[i];
功能类似:
int *f(); // f is a function that returns a pointer to int
...
x = *f(); // we must dereference the result of f() to get the int value
int (*f)(); // f is a pointer to a function that returns an int
...
x = (*f)(); // we must dereference f and execute the result to get the int value