在 C 和 C++ 中,* 绑定到 声明符,而不是类型说明符。在这两种语言中,声明都是基于表达式的类型,而不是对象。
例如,假设您有一个指向名为p 的int 的指针,并且您想访问p 指向的int 值;您可以通过使用一元 * 运算符取消引用指针来实现,如下所示:
x = *p;
表达式的类型*p是int;因此,p 的声明是
int *p;
无论您在同一声明语句中声明了多少个指针,这都是正确的;如果q 和r 也需要声明为指针,那么它们还需要将一元* 作为声明符的一部分:
int *p, *q, *r;
因为 表达式 *q 和 *r 的类型为 int。可以写T *p、T* p 或T * p 是C 和C++ 语法的一个意外;所有这些声明都将被解释为T (*p)。
这就是为什么我不喜欢将指针和引用类型声明为 C++ 风格的原因
T* p;
T& r;
因为它暗示了对 C 和 C++ 声明语法如何工作的错误看法,从而导致您刚刚遇到的那种确切的困惑。但是,我已经编写了足够多的 C++ 来意识到有时这种风格确实使代码的意图更加清晰,尤其是在定义容器类型时。
但还是错了。
这是对轨道中的 Lightness Races(以及反对我将 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,这种解释会更明显。是的,是的,是的,“每行只声明一件事,这不会有问题。”你知道还有什么办法不让它成为问题吗? 正确地编写你的声明符。声明器系统本身会更有意义,并且您出错的可能性会更小。
我们不是在争论这种语言的“古老的古怪”,它是语言语法及其哲学的基本组成部分。指针属性是 declarator 的一个属性,就像数组属性和函数属性一样,并且假装它以某种方式 不是 只会导致混淆并使 C 和 C++ 都更难比他们需要的理解。
我认为将解引用运算符设为一元而不是后缀是一个错误2,但这就是它在 B 中的工作方式,Ritchie 希望尽可能多地保留 B。我还认为 Bjarne 推广T* p 约定是一个错误。
- 在讨论的这一点上,有人会建议使用 typedef 像
typedef T arrtype[N];
arrtype* p;
这完全没有抓住重点,并在“C:完整参考”的第一版中赢得了建议者的殴打,因为它又大又重,对其他任何东西都没有好处。
- 与
T (*(*a)[N])() 相比,写T a*[N]*() 绝对不会让人眼花缭乱,而且扫描起来也更容易。