在 C 和 C++ 中,*
绑定到声明符,而不是类型说明符。在这两种语言中,声明都是基于表达式的类型,而不是对象。
例如,假设您有一个指向int
named的指针p
,并且您想要访问指向的 int
值p
;您可以通过使用一元运算符取消引用指针来做到这一点*
,如下所示:
x = *p;
表达式 的类型*p
是int
; 因此,声明p
是
int *p;
无论您在同一声明语句中声明了多少个指针,这都是正确的;如果q
并且r
还需要声明为指针,那么它们还需要将一元*
作为声明符的一部分:
int *p, *q, *r;
因为表达式 *q
和*r
有类型int
。这是 C 和 C++ 语法的意外,您可以编写T *p
、T* p
或T * p
; 所有这些声明都将被解释为T (*p)
.
这就是为什么我不喜欢将指针和引用类型声明为的 C++ 风格
T* p;
T& r;
因为它暗示了对 C 和 C++ 声明语法如何工作的错误看法,从而导致您刚刚遇到的那种混乱。但是,我已经编写了足够多的 C++ 来意识到有时这种风格确实使代码的意图更加清晰,尤其是在定义容器类型时。
但这仍然是错误的。
T* p
这是对轨道上的轻量级竞赛(以及反对我将公约标记为“错误”的任何其他人)的(迟了两年)的回应......
首先,您有一大堆问题,就像这个问题一样,专门由使用T* p
约定而引起,以及它如何不像人们期望的那样工作。这个网站上有多少问题是按“为什么不将两者都T* p, q
声明为指针?”的顺序?p
q
它引入了混乱——这本身就足以阻止它的使用。
但除此之外,它是不一致的。您无法将数组或函数与声明符分开,为什么要将指针与它分开?
“嗯,那是因为[]
and()
是后缀运算符,而 while*
是一元的”。是的,是的,那么为什么不将运算符与其操作数相关联呢?在声明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
。是的,是的,是的,“每行只声明一件事,这不会有问题。” 你知道还有什么办法不让它成为问题吗? 正确地编写你的声明器。声明器系统本身将更有意义,您将不太可能犯错误。
我们不是在争论这种语言的“古老古怪”,它是语言语法及其哲学的基本组成部分。Pointer-ness 是declarator的一个属性,就像 array-ness 和 function-ness 一样,假装它不仅会导致混淆,而且会使 C 和 C++ 都比它们需要的更难理解。
我会争辩说,将取消引用运算符设为一元而不是后缀是一个错误2,但这就是它在 B 中的工作方式,Ritchie 希望尽可能多地保留 B 。我还将争辩说,Bjarne 对T* p
公约的宣传是一个错误。
- 在讨论的这一点上,有人会建议使用 typedef
typedef T arrtype[N];
arrtype* p;
这完全没有抓住重点,并在“C:完整参考”的第一版中赢得了建议者的殴打,因为它又大又重,对其他任何东西都没有好处。
T a*[N]*()
与 相比,写作T (*(*a)[N])()
绝对不那么刺眼,并且更容易扫描。