在 C 和 C++ 中,*绑定到声明符,而不是类型说明符。在这两种语言中,声明都是基于表达式的类型,而不是对象。
例如,假设您有一个指向intnamed的指针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声明为指针?”的顺序?pq
它引入了混乱——这本身就足以阻止它的使用。
但除此之外,它是不一致的。您无法将数组或函数与声明符分开,为什么要将指针与它分开?
“嗯,那是因为[]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])()绝对不那么刺眼,并且更容易扫描。