类型声明 - 指针星号位置

36

在C++中,以下代码表示“为int指针分配内存”:

int* number;
所以,星号是变量类型的一部分;如果没有它,那就意味着“为int分配内存”。
那么,如果下面这个意思是“为两个int指针分配内存”,那不是更有意义吗?
int* number1, number2;

6
以下是 Stroustrup 针对同样问题给出的回答(原文链接:http://www.stroustrup.com/bs_faq2.html#whitespace):C++保留字中,只有 and、and_eq、bitand、bitor、compl、not、not_eq、or、or_eq、xor、xor_eq 这些单词中间可以包含空格。其他任何保留字都不能在其中包含空格。这条规则确保了程序代码的可读性,并且方便了编译器的解析。同时,也为使用 C++ 编写的工具(比如语法高亮插件和代码格式化工具等)提供了更好的支持。 - legends2k
5个回答

74

当被问及这个问题时,Stroustrup说(类似于以下内容)

  • 如果你更倾向于C语言风格,你会写int *aEmployee *pE (因此在你的脑海中,你会认为"a的内容是一个整数")
  • 如果你更倾向于C++语言风格,你会写int* aEmployee* pE (因此在你的脑海中,你会认为"a是一个整数指针")

你可以按照自己的喜好思考,只要不要在同一行声明两个指针。

对我而言,这个问题没什么。我是那种用Employee* pE的人,但我配偶是那种用Employee *pE的人。我的建议是不要太过于纠结。


31
实际上,星号(*)是附加在变量名上的(这是从C语言继承下来的惯例),因此
int * number1, number2;

声明 number1 为指向 int 类型的指针(即 *number1 是一个 int),number2int 类型。
空格对于输入 number 没有影响,它只是作为一个标记分隔符。以下所有内容对编译器来说都是相同的,因为解析后空格将被删除。
int *a;
int*a;
int* a;
int * a;
int/**a***/*/*a***/a;

使用

int* number1, *number2;

为了避免混淆,可以创建两个指针,或者更好的方法是将其拆分为多个声明。

int* number1;
int* number2;

谢谢,但我不是在问我应该如何编写代码。我在质疑编译器为什么被设计成那样。是什么让这种语法更加合理。 - Feyyaz
13
@sahs说:“除了‘一直以来都是这样’之外,没有什么好的理由能解释它为什么是这个样子。这并不是很合乎逻辑,而是基于历史原因。C语言是我们继承的声明语法,它已经有40多年的历史,早在现今被认为是好的语言设计的很多元素出现之前。” - sbi
太棒了!这解决了我一万年的困扰。但是,G,为什么人们经常混淆指针和非指针呢... - TRoa

21

你过于简化了C++声明的结构(尽管你所说的是完全合理的)。乍一看,C++声明似乎只包含类型名称和一系列逗号分隔的实体名称,但实际上在C++(以及C)中,声明实际上由类型名称和声明符序列组成。您声明的实体的完整类型信息分别位于两个独立的位置,并且其部分信息实际上是其声明符的一部分。这就是C++的本质。为了更好地反映声明的实际结构(从语言的角度来看),将声明格式化为以下格式可能是一个好主意:

int *a, *b;

即明确将*与实体名称分组,而不是类型名称。(但是最终这是个人偏好的问题。)

至于为什么语言是这样设计的,正如您在其中一个评论中提问的那样...正如您所知,声明语法的部分描述正在声明的实体的类型可以出现在名称的左侧(例如*&)以及其右侧(例如()[]),如下所示

int *f(), (&g)[5], h[2][2];

对于出现在右侧的位,没有其他的选择。它们必须与实体名称分组,而不是类型名称。可能会进一步问为什么上述声明不是用C++完成的。

int *() f;
int (&)[5] g;
int [2][2] h;

也就是说,所有与类型相关的内容都会以紧凑的组合形式出现在左侧。... 好吧,对于这个问题的答案就是C++采用了这种方式。这种方法是从C语言继承而来的,在那里,“声明必须类似于使用”通常被引用为其理由。

另外需要记住的一件事(并且经常被错误地解释)是,限定符与类型名相邻的声明属于公共类型,而不是第一个单独的声明符的一部分。例如:

int const *a, *b;

声明了const int *aconst int *b,而不是一些人错误地认为的int *b。因此,我个人更喜欢使用更合乎逻辑的

const int *a, *b;

尽管我更喜欢将*与名称一起分组而不是与类型一起排序,但仍需要排序。


似乎更合理的做法是将 *&() [] 与变量名分组,因为它们描述了 变量,尽管它们修改了其类型,但并不影响同一行中所有其他变量的类型;同样,限定符与在该行中声明的所有变量相关联,因此在类型之前加上它们似乎是正确的。我一直在不考虑原理的情况下这样做,现在我知道了,所以我会继续坚持这种做法,即使有人认为我是一个伪装成 C++ 程序员的 C 程序员。 - legends2k
Marshall Cline在回答一个类似的常见问题时使用讽刺的语气来指出这种偏见是错误的。该问题链接:http://www.parashift.com/c++-faq/void-in-param-list.html(请看结尾部分)。 - legends2k

8
这只是C语言声明语法的许多不规则之一。类型修饰符*是类型的一部分,但在语法上它属于声明的标识符。
顺便说一句,&[]也是如此。
有关*除了修改类型外还有什么作用,请参见此处

这不是C语法的异常情况,而是声明遵循使用的常规语法的一部分。这个答案基于对C语法的误解,即声明看起来像:<type> <name>。这不是C声明语法的工作方式,如果你认为是这样的话,你会反复感到困惑并发现不规则性。 - Peaker
2
@Peaker:我在1992年左右开始讨厌C语言的声明语法。20年前,我第一次用C++编写代码(采用了C语言的声明语法)赚到了钱。然而,习惯和长达二十多年的经常使用使我能够立即理解(大部分)它,以及我喜欢C++并不妨碍我认识到声明语法是一个彻底的失败,只能通过其巨大的古老性来辩解。你也应该学会区分通过不断接触而习惯于的坏东西和可以立即被认可的好东西。继续学习吧! - sbi

3
不,
int* number1, number2;

声明number1为指向int的指针,将number2声明为int类型。个人而言,我更喜欢将星号附加到类型上,这在您提问中所涉及的问题中也存在一些小陷阱,因此我将按照以下方式声明两个指针:

int* number1;
int* number2;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接