C中的以下声明:
int* a, b;
会将a
声明为int*
类型,将b
声明为类型int
。我很清楚这个陷阱,但我想知道的是为什么它以这种方式工作。为什么不像大多数人直觉所期望的那样将b
声明为int*
?换句话说,为什么*
适用于变量名称,而不是类型?
当然,你可以这样写,以便与实际的工作原理更加一致:
int *a, b;
但是,我和我所说过的每个人都认为 a是“指向int”的类型,而不是 a是指向某些数据的指针和类型该数据为“int”。
这对C的设计师来说简直是一个糟糕的决定,还是有一些很好的理由为什么它会以这种方式解析?我确定之前已经回答了这个问题,但我似乎无法通过搜索找到它。
答案 0 :(得分:31)
C语句以这种方式编写,以便“声明镜像使用”。这就是你声明这样的数组的原因:
int a[10];
你是否拥有你提出的规则,总是
type identifier, identifier, identifier, ... ;
...那么数组逻辑上必须像这样声明:
int[10] a;
这很好,但不反映使用 a
的方式。请注意,这也适用于函数 - 我们声明这样的函数:
void foo(int a, char *b);
而不是
void(int a, char* b) foo;
通常,“声明镜像使用”规则意味着您只需要记住一组关联性规则,这些规则适用于*
,[]
和()
等运算符。您正在使用该值以及*
,[]
和()
等声明符中的相应标记。
经过深思熟虑后,我认为值得指出的是,拼写“指向int 的指针”为“int*
”只是“声明镜像使用”的结果。如果你打算使用另一种声明方式,将“指向int ”的指针拼写为“&int
”,或者像“{{1}”这样完全不同的东西可能会更有意义。 }”。
答案 1 :(得分:17)
The Development of the C Language上有一个网页,上面写着:“这些声明的语法反映了i,* pi和** ppi在表达式中使用时都会产生int类型的观察结果。”在页面上搜索该句子以找到谈论此问题的相关部分。
答案 2 :(得分:9)
可能还有其他历史原因,但我总是这样理解:
一种声明,一种类型。
如果a,b,c和d必须与此类型相同:
int a, b, c, d;
然后行上的所有内容也必须是整数。
int a, *b, **c, ***d;
4个整数:
它也可能与运算符优先级有关,或者它可能在过去的某个时刻。
答案 3 :(得分:2)
*
修改变量名称,而不是类型说明符。这主要是因为*
被解析的方式。请听这些陈述:
char* x;
char *x;
这些陈述是等同的。 *
运算符需要在类型说明符和变量名之间(它被视为中缀运算符),但它可以位于空格的任一侧。鉴于此,宣言
int* a, b;
不会使b
成为指针,因为它旁边没有*
。 *
仅对其两侧的对象进行操作。
另外,请考虑这种方式:当您编写声明int x;
时,表明x
是一个整数。如果y
是指向整数的指针,则*y
是整数。当您编写int *y;
时,表示*y
是一个整数(这是您想要的)。在语句char a, *b, ***c;
中,您指出变量a
,b
的解除引用值以及c
的三重取消引用值都是{{1}类型}}。以这种方式声明变量使得星号运算符的使用(几乎)与解除引用一致。
我同意它反过来更有意义。为了避免这个陷阱,我总是把自己作为一条规则来自己在一条线上声明指针。
答案 4 :(得分:2)
我认为它与类型修饰符的完整声明语法有关:
int x[20], y;
int (*fp)(), z;
在这些示例中,更明显的是修饰符只影响其中一个声明。一种猜测是,一旦K& R决定以这种方式设计修饰符,让修饰符仅影响一个声明感觉“正确”。
另一方面,我建议将每个声明限制为一个变量:
int *x;
int y;
答案 5 :(得分:1)
因为如果声明
int* a, b;
也要将b
声明为指针,那么你就没有办法宣布
int* a;
int b;
在一条线上。
另一方面,你可以做到
int*a, *b;
得到你想要的东西。
这样想:它现在的方式仍然是最简洁而又独特的方式。那就是C主要是关于:)
答案 6 :(得分:1)
考虑声明:
int *a[10];
int (*b)[10];
第一个是十个整数指针的数组,第二个是指向十个整数数组的指针。
现在,如果*附加到类型声明,则在它们之间放置一个括号在语法上是无效的。所以你必须找到另一种方法来区分这两种形式。
答案 7 :(得分:0)
我只能猜测为什么必须为一行上提到的每个变量名重复*
以使它们成为所有指针。 也许这是由于对语言一致性的决定?让我用一个例子来说明这一点:
假设你有一个函数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声明语法,因为它是)。
现在,也许有人认为,如果有必要为所有变量重复类型声明的情况,也许这应该只是一般情况。
(不要忘记我只是在这里猜测。)