C中的指针语法:为什么*仅适用于第一个变量?

C中的以下声明:

int* a, b; 

将声明a类型为int*b作为int类型。 我很清楚这个陷阱,但我想知道的是它为什么会这样运作。 为什么不像大多数人直觉所期望的那样将b声明为int* ? 换句话说,为什么*适用于变量名,而不是类型?

当然你可以用这种方式写它,以便与它的实际工作方式更加一致:

 int *a, b; 

但是,我和我所说过的所有人都认为a是“指向int”的类型 ,而不是a是指向某些数据的指针 ,而该数据的类型是“int”

这对C的设计者来说简直是一个糟糕的决定还是有一些很好的理由为什么它会被解析? 我确定之前已经回答了这个问题,但我似乎无法使用搜索找到它。

有一个关于C语言开发的网页说:“这些声明的语法反映了在表达式中使用i,* pi和** ppi都会产生int类型的观察结果。” 在页面上搜索该句子以查找关于此问题的相关部分。

C语句以这种方式编写,以便“声明镜像使用”。 这就是你声明这样的数组的原因:

 int a[10]; 

你是否拥有你提出的规则,总是在哪里

 type identifier, identifier, identifier, ... ; 

…那么数组逻辑上必须像这样声明:

 int[10] a; 

这很好,但不反映你如何使用 。 请注意,这也适用于函数 – 我们声明这样的函数:

 void foo(int a, char *b); 

而不是

 void(int a, char* b) foo; 

通常,“声明镜像使用”规则意味着您只需要记住一组关联性规则,当您使用该值时,这些规则适用于*[]()等运算符,以及声明符中的相应标记像*[]()


经过深思熟虑之后,我认为值得指出的是,将“ 指向int ”的拼写为“ int* ”只是“声明镜子使用”的结果。 如果你打算使用另一种声明方式,将“ 指向int ”的拼写为“ &int ”或者像“ @int ”这样完全不同的东西可能会更有意义。

可能还有其他历史原因,但我总是这样理解:

一种声明,一种类型。

如果a,b,c和d必须与此类型相同:

 int a, b, c, d; 

然后,该行上的所有内容也必须是整数。

 int a, *b, **c, ***d; 

4个整数:

  1. 一个
  2. * b
  3. **C
  4. *** d

它也可能与运算符优先级有关,或者它可能在过去的某个时刻。

我假设它与类型修饰符的完整声明语法有关:

 int x[20], y; int (*fp)(), z; 

在这些示例中,更明显的是修饰符仅影响其中一个声明。 一个猜测是,一旦K&R决定以这种方式设计修饰符,让修饰符仅影响一个声明感觉“正确”。

另外,我建议将每个声明限制为一个变量:

 int *x; int y; 

*修改变量名称,而不是类型说明符。 这主要是因为解析*的方式。 拿这些陈述:

 char* x; char *x; 

这些陈述是等同的。 *运算符需要在类型说明符和变量名之间(它被视为中缀运算符),但它可以位于空格的任一侧。 鉴于此,宣言

 int* a, b; 

不会使b成为指针,因为它没有*相邻。 *仅对其两侧的对象进行操作。

另外,按照这种方式思考:当你编写声明int x; ,表示x是整数。 如果y是指向整数的指针,则*y是整数。 当你写int *y; ,表示*y是一个整数(这是你想要的)。 在声明char a, *b, ***c; ,您指示变量ab的解除引用值和c的三重取消引用值都是char类型。 以这种方式声明变量使得星形运算符的使用(几乎)与解除引用一致。

我同意,反过来几乎更有意义。 为了避免这个陷阱,我总是让自己成为一个规则来自己声明一条线上的指针。

因为如果声明

 int* a, b; 

如果要将b声明为指针,那么你将无法声明

 int* a; int b; 

在一条线上。

另一方面,你可以做到

 int*a, *b; 

得到你想要的。

想一想:它现在的方式仍然是最简洁但又独特的方式。 这就是C主要是关于:)

考虑一下声明:

 int *a[10]; int (*b)[10]; 

第一个是十个整数指针的数组,第二个是指向十个整数数组的指针。

现在,如果*附加到类型声明,则在它们之间放置括号将在语法上无效。 所以你必须找到另一种方法来区分这两种forms。

我只能猜测为什么必须为一行中提到的每个变量名重复*以使它们成为所有指针。 也许这是由于语言一致性的决定? 让我用一个例子来说明这一点:


假设你有一个函数foo声明如下:

 int foo() { ... } 

我们还假设您要声明两个函数指向此函数:

 int (*fooptr1, fooptr2)(); // this doesn't work; and even if it did, what would the syntax possibly // look like to initializing them to function foo? // int (*fooptr1 = foo, fooptr2 = foo)() ? int (*fooptr1)() = foo, (*fooptr2)() = foo; // this works. 

在这种情况下,您只需要为两个变量重复整个类型声明,并且您不会出错,因为没有其他方法可以执行此操作(因为它具有C声明语法)。


现在,也许有人认为,如果有必要为所有变量重复类型声明的情况,也许这应该只是一般情况。

(不要忘记我只是在猜这里。)