星号在指针声明中的位置


92

我最近决定,我只需要最终学习C / C ++,关于指针或更确切地说,它们的定义,我并没有真正了解的一件事。

这些示例如何:

  1. int* test;
  2. int *test;
  3. int * test;
  4. int* test,test2;
  5. int *test,test2;
  6. int * test,test2;

现在,据我所知,前三个案例都在做同样的事情:Test不是一个int,而是一个指向int的指针。

第二组示例比较棘手。在情况4中,test和test2都是指向int的指针,而在情况5中,只有test是指针,而test2是“真实” int。情况6呢?与情况5相同?


10
在C / C ++中,空格不会改变含义。
苏珊(Sulthan)2012年

19
7 int*test;
Jin Kwon

3
+1是因为我只想问1-3。读这个问题使我知道了4-6的一些我从未想过的东西。
vastlysuperiorman

@Sulthan 99%的时间是正确的,但并非总是如此。在我脑海中,在模板类型空间要求(C ++ 11之前)中有模板类型的类型。在Foo<Bar<char>>>>必须写成> >不被视为右移。
AnorZaken

3
@AnorZaken您说得对,这是一个相当古老的评论。在多种情况下,空格将改变含义,例如,增量++运算符不能被空格分开,标识符不能被空格分开(并且结果对于编译器仍然是合法的,但具有未定义的运行时行为)。考虑到C / C ++的语法混乱,很难定义确切的情况。
苏珊(Sulthan)2016年

Answers:


129

4、5和6是同一件事,只是测试是一个指针。如果要两个指针,则应使用:

int *test, *test2;

或者,甚至更好(使一切清晰):

int* test;
int* test2;

3
那么案例4实际上是一个陷阱吗?有没有说明或解释为什么int * test,test2仅使第一个变量成为指针的规范?
Michael Stum

8
@ Michael Stum这是C ++,所以您真的认为有一个合乎逻辑的解释吗?
乔·菲利普斯

6
阅读K&R(C编程语言)。它非常清楚地解释了所有这些。
Ferruccio

8
案例4、5和6是“死亡陷阱”。这就是为什么许多C / C ++样式指导建议每个语句仅一个声明的原因之一。
Michael Burr

14
空格对于C编译器而言无关紧要(忽略预处理器)。因此,无论星号与其周围环境之间有多少空间,它的含义都完全相同。
迅速

45

星号周围的空白不重要。这三个含义相同:

int* test;
int *test;
int * test;

int *var1, var2”是一种邪恶的语法,仅是为了使人们感到困惑,应避免使用。它扩展为:

int *var1;
int var2;

16
别忘了int*test
Mateen Ulhaq

1
星号之前或之后的空间只是美学问题。但是,Google Coding标准随附于int *testgoogle-styleguide.googlecode.com/svn/trunk/…)。保持一致

@SebastianRaschka Google C ++样式指南明确允许使用任何一个星号。自您阅读以来,它可能已更改。
杰里德·贝克

33

许多编码指南建议您每行只声明一个变量。这样可以避免您在问这个问题之前感到困惑。与我合作的大多数C ++程序员似乎都坚持这一点。


我知道一点,但我发现有用的是向后读取声明。

int* test;   // test is a pointer to an int

这开始工作得很好,尤其是当您开始声明const指针时,要弄清是指针是const还是指针所指向的东西是const变得很棘手。

int* const test; // test is a const pointer to an int

int const * test; // test is a pointer to a const int ... but many people write this as  
const int * test; // test is a pointer to an int that's const

尽管“每行一个变量”似乎很有用,但我们仍未完全解决星号偏左或偏右的情况。我很确定,在狂野的代码中,一种变体会流行。有点像有些国家在右边开车,而其他国家在错误的方向开车,例如英国。;-)
Shevy

不幸的是,从冒险到野外,我看到了很多两种风格。现在,在我的团队中,我们使用clang格式和我们已经同意的样式。至少这意味着我们的团队生成的所有代码在空白处都具有相同的样式。
Scott Langham

33

使用“顺时针螺旋规则”有助于解析C / C ++声明;

遵循三个简单步骤:

  1. 从未知元素开始,沿螺旋/顺时针方向移动;遇到以下元素时,请用相应的英语声明替换它们:

    [X][]:数组X大小为...或数组未定义大小为...

    (type1, type2):函数传递type1和type2返回...

    *:指向...的指针

  2. 继续沿螺旋/顺时针方向执行此操作,直到所有令牌都被覆盖。
  3. 始终首先解决括号中的任何问题!

另外,在可能的情况下,声明应放在单独的语句中(在大多数情况下是这样)。


4
遗憾的是,这看起来令人生畏并且非常可怕。
乔·菲利普斯

7
的确如此,但是对于某些更复杂的结构来说,这似乎是一个很好的解释
Michael Stum

@ d03boy:毫无疑问-C / C ++声明可能是一场噩梦。
Michael Burr

2
“螺旋”没有任何意义,更不用说“顺时针”了。我宁愿将其命名为“左右规则”,因为语法不会使您看起来从右下到左上,而只能从右到左。
罗斯兰

我将此称为“左右左右”规则。C ++人士经常喜欢假装所有类型信息在左侧,这导致了int* x;样式而不是传统int *x;样式。当然,间距对编译器无关紧要,但确实会影响人类。拒绝实际语法会导致样式规则,这些规则会使读者感到烦恼和困惑。
阿德里安·麦卡锡

12

如其他提到的,4,5和6是相同的。通常,人们使用这些示例来进行论证“ *属于”属于变量而不是类型。尽管这是样式问题,但对于您是否应该以这种方式思考和编写它存在一些争论:

int* x; // "x is a pointer to int"

或者这样:

int *x; // "*x is an int"

FWIW我在第一阵营,但是其他人为第二种形式争论的原因是它(主要)解决了这个特定问题:

int* x,y; // "x is a pointer to int, y is an int"

这可能会产生误导;相反,你会写

int *x,y; // it's a little clearer what is going on here

或者如果您真的想要两个指针,

int *x, *y; // two pointers

就我个人而言,我说将其保留为每行一个变量,那么无论您喜欢哪种样式都没关系。


6
这是假的,你叫int *MyFunc(void)什么?一个*MyFunc函数返回一个int?没有。显然,我们应该写int* MyFunc(void),说MyFunc是一个返回a的函数int*。因此,对我来说很明显,C和C ++语法解析规则对于变量声明完全是错误的。它们应该已经将指针限定符作为整个逗号序列的共享类型的一部分。
v.oddou

1
但是*MyFunc() 一个int。C语法的问题在于混合使用前缀后缀语法-如果仅使用后缀,则不会造成混淆。
Antti Haapala '18

1
第一个阵营与语言的语法作斗争,导致诸如这样的构造令人困惑int const* x;,我发现这种构造令人误解a * x+b * y
阿德里安·麦卡锡


5

在4、5和6中,test始终是一个指针,test2而不是一个指针。在C ++中,空格几乎(几乎)不重要。


3

在我看来,答案都是根据情况而定。通常,IMO,最好在指针名称旁边而不是类型旁边加上星号。比较例如:

int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2;  // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors

为什么第二种情况不一致?因为eg int x,y;声明了两个相同类型的变量,但是该类型在声明中仅被提及一次。这创建了先例和预期行为。并且int* pointer1, pointer2;与之不一致,因为它声明pointer1为指针,但它pointer2是一个整数变量。显然容易出错,因此应避免(通过在指针名称而不是类型旁边放置星号)。

但是,在某些例外情况下,您可能无法将星号放在对象名称旁边(并且在您放置它的位置很重要)而又不会得到不希望的结果,例如:

MyClass *volatile MyObjName

void test (const char *const p) // const value pointed to by a const pointer

最后,在某些情况下,将星号放在类型名称旁边可能更清晰一些,例如:

void* ClassName::getItemPtr () {return &item;} // Clear at first sight


2

C语言的基本原理是您以使用变量的方式声明变量。例如

char *a[100];

说那 *a[42]将是一个char。和a[42]一个char指针。因此a是一个char指针数组。

这是因为原始的编译器作者希望对表达式和声明使用相同的解析器。(选择语言设计不是很明智的原因)


然而,写作char* a[100]; 推论出这*a[42];将是a chara[42];char指针。
yyny16年

好吧,我们都得出相同的结论,只是顺序是变化的。
Michel Billaud,

Quote:“说* a [42]将是一个字符。而a [42]将是一个字符指针”。您确定不是相反的方式吗?
19:30解锁

如果您更喜欢另一种方式,请说a[42]是一个char指针,并且*a[42]是一个字符。
Michel Billaud,

2

我会说最初的约定是将星形放在指针名称的一侧(声明的右侧)。

您可以遵循相同的规则,但是如果在类型方面加星号,则没什么大不了的。请记住,一致性很重要,因此无论选择哪一面,始终保持恒星不变。


很好-解析器似乎允许任何一种变体,但是如果Dennis和Linus说它应该在右侧,那将非常引人注意。但是,我们仍然缺乏一些基本原理,因此也缺少这样做的理由。这有点像制表符与空间的情况-除​​了解决了一个问题之外,因为根据StackOverflow的说法,使用空格而不是制表符的人可以赚到更多钱... :-)
Shevy

1

指针是该类型的修饰符。最好从右到左阅读它们,以更好地理解星号如何修改类型。“ int *”可以理解为“指向int的指针”,在多个声明中必须指定每个变量都是一个指针,否则它将被创建为标准变量。

1,2和3)测试属于(int *)类型。空格无关紧要。

4,5和6)测试属于(int *)类型。Test2的类型为int。同样,空格是无关紧要的。


-3

一个好的经验法则是,许多人似乎通过以下方式来掌握这些概念:在C ++中,许多语义是通过关键字或标识符的左键绑定而得出的。

举个例子:

int const bla;

const适用于“ int”一词。指针的星号也是如此,它们适用于它们左侧的关键字。以及实际的变量名称?是的,这是用剩下的来声明的。


1
这不能回答问题。更糟糕的是,如果我们尝试从中推断出答案,则意味着星号绑定到其左侧的类型,正如其他所有人所说的那样,它是错误的。它绑定到其右侧的单个变量名称。
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.