在C和C++中,
*
将绑定到
declarator而非类型说明符。在这两种语言中,声明是基于
表达式的类型而非对象的。
例如,假设您有一个指向名为
p
的
int
类型的指针,并且您想要访问
p
指向的
int
值。您可以使用一元运算符
*
对指针进行解引用操作来实现。
x = *p
*p
的类型是
int
,因此
p
的声明为:
int *p;
无论在同一声明语句中声明了多少指针,这都是真实的;如果需要将q和r声明为指针,那么它们也需要在声明符中加上一元运算符
*
。
int *p, *q, *r;
因为表达式
*q和
*r的类型是
int
,所以会出现这种情况。在C和C++语法中,你可以写成
T *p
、
T* p
或
T * p
,这只是C和C++语法的偶然性;所有这些声明都会被解释为
T (*p)
。
这就是为什么我不喜欢C++风格的指针和引用类型声明的原因。
T* p;
T& r;
因为它意味着对C和C++声明语法工作方式的错误看法,导致了你刚刚遇到的
确切混淆。然而,我写过足够多的C++,意识到在定义容器类型时,有时候这种风格确实可以使代码的意图更清晰。
但它仍然是不正确的。
这是(两年晚了)对Lightness Races in Orbit(以及任何反对我将
T* p
规则称为“错误”的人)的回应……
首先,正是由于使用
T* p
规则而引起了像这样的问题和
特别是产生了像这个问题一样的问题,以及它不能按照人们的期望工作。这个网站上有多少关于“为什么
T* p,q
不会将
p
和
q
都声明为指针?”这样的问题?
它
引入了混乱——仅仅这一点就足以阻止它的使用。
但除此之外,它是
不一致的。你无法将数组性或函数性与声明符分开,那你为什么要将指针性与它分开?
“嗯,那是因为
[]
和
()
是后缀运算符,而
*
是一元的。”是的,它是一元的,那么
为什么你不将运算符与其操作数关联起来?在声明
T* p
中,
T
不是
*
的操作数,为什么我们要像它是一样写声明呢?
如果
a
是“指针数组”,为什么我们要写
T* a[N]
?如果
f
是“返回指针的函数”,为什么我们要写
T* f()
?如果你把这些声明写成
T *a[N]
和
T *f()
,那么声明符系统就
更有意义和
内部一致性。这从我可以使用
T
作为任何类型(实际上,对于任何一组声明说明符)的替代品这一事实中应该是显而易见的。
然后你有指向数组和指向函数的指针,其中
*
必须明确绑定到声明符
1:
T (*a)[N];
T (*f)();
是的,指针性是您声明的事物的重要属性,但数组性和函数性也同样重要,强调其中之一会带来比解决问题更多的问题。正如这个问题所显示的那样,
T* p
惯例
引入了混淆。
因为
*
是一元运算符并且是一个单独的标记,您可以编写
T* p
、
T *p
、
T*p
和
T * p
,它们都将被编译器接受,但它们都将被解释为
T (*p)
。更重要的是,
T* p, q, r
将被解释为
T (*p), q, r
。如果您编写
T *p, q, r
,那种解释更加明显。是的,“每行只声明一个东西就不会有问题。”,但是您知道还有另外一种方法吗?
正确编写您的声明符。声明符系统本身将变得更加清晰,您也不太可能犯错误。
我们不是在争论语言的“古怪”,这是语言语法和哲学的基本组成部分。指针性是
声明符的属性,就像数组性和函数性一样,假装它在某种程度上
不是会导致混淆,并使 C 和 C++ 比需要理解的更困难。
我认为将取消引用运算符设为一元运算符而不是后缀运算符是一个错误
2,但这是 B 中的工作方式,Ritchie 想尽可能保留 B 的许多特点。我还会认为 Bjarne 推广
T* p
惯例是一个错误。
- At this point in the discussion, somebody will suggest using a typedef like
typedef T arrtype[N]
arrtype* p
which just totally misses the point and earns the suggester a beating with the first edition of "C: The Complete Reference" because it's big and heavy and no good for anything else.
- Writing
T a*[N]*()
as opposed to T (*(*a)[N])()
is definitely less eye-stabby and scans much more easily.