为什么变量名之前的星号,而不是类型之后?

时间:2008-12-29 19:22:25

标签: c pointers variables naming-conventions

为什么大多数C程序员都将这样的变量命名为:

int *myVariable;

而不是像这样:

int* myVariable;

两者都有效。在我看来,星号是类型的一部分,而不是变量名称的一部分。任何人都可以解释这个逻辑吗?

13 个答案:

答案 0 :(得分:218)

它们完全等效。 但是,在

int *myVariable, myVariable2;

似乎很明显,myVariable的类型为 int * ,而myVariable2的类型为 int 。 在

int* myVariable, myVariable2;

两者的类型 int * 似乎很明显,但这不正确,因为myVariable2的类型为 int

因此,第一种编程风格更直观。

答案 1 :(得分:107)

如果从另一个角度来看,*myVariable的类型为int,这是有道理的。

答案 2 :(得分:39)

因为*更接近变量而不是类型:

int* varA, varB; // This is misleading

然而,即使是最好的单行声明对我来说也是违反直觉的,因为*是该类型的一部分。我喜欢这样做:

int* varA;
int varB;

与往常一样,代码越小,可读性越强。 ;)

答案 3 :(得分:25)

到目前为止,没有人在这里提到的是这个星号实际上是C中的“取消引用运算符”。

*a = 10;

上述行并不意味着我想将10分配给a,这意味着我想将10分配给a指向的任何内存位置。我从未见过有人写过

* a = 10;
你有吗?因此,解除引用运算符几乎总是在没有空格的情况下编写。这可能是为了将它与跨越多行的乘法区分开来:

x = a * b * c * d
  * e * f * g;

这里*e会产生误导,不是吗?

好的,现在以下行实际意味着什么:

int *a;

大多数人会说:

这意味着a是指向int值的指针。

这在技术上是正确的,大多数人喜欢以这种方式查看/阅读它,这就是现代C标准定义它的方式(注意语言C本身早于所有ANSI和ISO标准)。但这不是看待它的唯一方式。您还可以按如下方式阅读此行:

a的解除引用值属于int类型。

因此实际上,此声明中的星号也可以看作是取消引用运算符,它也解释了它的位置。并且a是一个指针实际上并没有真正声明,事实是隐含的,你实际上只能解引用的是指针。

C标准仅为*运算符定义了两个含义:

  • 间接运营商
  • 乘法运算符

间接只是单一含义,没有额外的意义来声明指针,只有间接,这是解除引用操作所做的,它执行间接访问,因此也在{{1}之类的语句中这是间接的访问int *a;表示间接访问),因此上面的第二个语句比第一个语句更接近标准。

答案 4 :(得分:17)

我将在这里说明,对这个问题有一个直接的答案,无论是变量声明还是参数和返回类型,这都是星号应该转到名称旁边:int *myVariable;。要了解原因,请查看如何在C中声明其他类型的符号:

int my_function(int arg);用于函数;

float my_array[3]表示数组。

一般模式,称为声明后使用,是符号的类型被分割为名称前面的部分,而部分名称,以及名称周围的这些部分模仿您将用于获取左侧类型值的语法:

int a_return_value = my_function(729);

float an_element = my_array[2];

和:int copy_of_value = *myVariable;

C ++在使用引用时会抛出一个扳手,因为在使用引用时的语法与值类型的语法相同,所以你可以说C ++对C采用了不同的方法。另一方面,C ++在指针的情况下保留了相同的C行为,因此在这方面引用确实是奇怪的。

答案 5 :(得分:11)

这只是一个偏好问题。

当您阅读代码时,在第二种情况下区分变量和指针会更容易,但是当您将一个常见类型的变量和指针放在一行中时,可能会导致混淆(本身通常不鼓励这样做)项目指南,因为降低了可读性。)

我更喜欢在类型名称旁边声明带有相应符号的指针,例如

int* pMyPointer;

答案 6 :(得分:5)

在K& R中,它们将指针运算符放在变量名称旁边而不是类型。就个人而言,我认为这本书是最终的C权威/圣经。同样来自Objective-C背景,我遵循* name约定。

答案 7 :(得分:2)

因为当你有如下声明时更有意义:

int *a, *b;

答案 8 :(得分:2)

为了在一行中声明多个指针,我更喜欢int* a, * b;,它更直观地将“a”声明为指向整数的指针,并且在同样声明“b”时不会混合样式。就像有人说的那样,我不会在同一个声明中声明两种不同的类型。

答案 9 :(得分:1)

我已经回答了CP中的类似问题,并且,因为没有人提到过,我也必须指出 C是一种自由格式语言,无论你选择什么样的风格,解析器可以区分每个令牌。 C的这种特殊性导致了一种非常特殊的比赛,称为C obfuscation contest

答案 10 :(得分:1)

喜欢int* x;的人们正试图将他们的代码带入一个虚构的世界,在虚构的世界中,类型在左侧,标识符(名称)在右侧。

我之所以说“虚构”,是因为:

  

在C和C ++中,通常情况下,声明的标识符被类型信息包围。

这听起来很疯狂,但您知道这是真的。以下是一些示例:

  • int main(int argc, char *argv[])的意思是“ main是一个函数,它接受int和指向char的指针数组并返回int。换句话说,类型信息的 most 位于右侧。有些人认为函数声明不算什么,因为它们在某种程度上是“特殊的”。好,让我们尝试一个变量。

  • void (*fn)(int)意味着fn是指向一个接受int并且不返回任何内容的函数的指针。

  • int a[10]声明'a'为10 int个数组。

  • pixel bitmap[height][width]

  • 很显然,我已经摘录了一些示例,这些示例的右边都有很多类型信息,以阐明我的观点。在很多声明中,大多数(如果不是全部)类型都在左侧,例如struct { int x; int y; } center

此声明语法源自K&R希望声明能够反映用法的愿望。读取简单的声明很直观,可以通过学习左右规则(有时称为螺旋规则或仅称为左右规则)来掌握更复杂的声明。

C非常简单,以至于许多C程序员都采用这种风格,并以int *p的形式编写简单的声明。

在C ++中,语法变得有些复杂(带有类,引用,模板,枚举类),并且作为对这种复杂性的反应,您会发现在许多声明中要花更多的精力将类型与标识符分离。换句话说,如果您查看大量的C ++代码,则可能会看到更多int* p样式的声明。

在任何一种语言中,您可以始终在 variable 声明的左侧使用类型,因为(1)从不在同一条语句中声明多个变量,并且(2 )使用typedef(或别名声明,具有讽刺意味的是,将别名标识符放在类型的左侧)。例如:

typedef int array_of_10_ints[10];
array_of_10_ints a;

答案 11 :(得分:0)

本主题中的许多参数都是主观的,而有关“星星绑定到变量名”的参数是幼稚的。这里有一些不只是观点的论点:


被遗忘的指针类型限定符

形式上,“星号”既不属于类型也不属于变量名,它是其自己的名为 pointer 的语法项的一部分。正式的C语法(ISO 9899:2018)为:

  

(6.7)声明:
    声明说明符 init-declarator-list opt ;

其中声明说明符包含类型(和存储),而 init-declarator-list 包含指针和变量名。如果进一步剖析该声明符列表语法,将会看到以下内容:

  

(6.7.6)声明符:
    pointer opt 直接声明符
  ...
  (6.7.6)指针:
  * 类型限定符列表 opt
  * 类型限定符列表 opt 指针

声明符是整个声明,直接声明符是标识符(变量名),指针是星号,后跟属于指针本身的可选类型限定符列表。

使有关“星星属于变量”的各种样式参数不一致的原因是,他们忘记了这些指针类型限定符。 int* const xint *const xint*const x

考虑int *const a, b;ab的类型是什么? “恒星属于变量”不再那么明显了。相反,人们会开始思考const所属的地方。

您可以肯定地说出星星属于指针类型限定符,但除此之外没有太多。

对于使用int *a样式的指针,指针的类型限定符列表可能会导致问题。那些在typedef中使用指针的人(我们不应该这样做,这是非常不好的做法!),并认为“星星属于变量名”往往会写出这个非常细小的错误:

    /*** bad code, don't do this ***/
    typedef int *bad_idea_t; 
    ...
    void func (const bad_idea_t *foo);

这可以干净地编译。现在您可能会认为代码是const正确的。不是这样!这段代码偶然是伪造的const正确性。

foo的类型实际上是int*const*-最外面的指针是只读的,而不是指向数据的指针。因此,在此函数内,我们可以执行**foo = n;,它将更改调用方中的变量值。

这是因为在表达式const bad_idea_t *foo中,*在这里不属于变量名!在伪代码中,此参数声明应读取为const (bad_idea_t *) foo,而不是读取为(const bad_idea_t) *foo。在这种情况下,星号属于隐藏的指针类型-类型是指针,并且const限定的指针写为*const

但是,在上面的示例中,问题的根源是在typedef而不是*样式后面隐藏指针的做法。


关于在一行上声明多个变量

在一行上声明多个变量被广泛认为是错误的做法 1)。 CERT-C很好地总结为:

  

DCL04-C。每个声明不要声明多个变量

只需阅读英语,然后常识就同意 a 声明应为 one 声明。

并且变量是否为指针都没有关系。在一行中声明每个变量几乎可以使代码更清晰。

因此,关于程序员对int* a, b感到困惑的论点是不好的。问题的根源是使用多个声明符,而不是*的位置。无论样式如何,您都应该编写以下代码:

    int* a; // or int *a
    int b;

另一个声音但主观的论点是,给定int* a的类型a毫无疑问是int*,所以星星属于类型限定符。

但是基本上我的结论是,这里发表的许多论点只是主观的和幼稚的。对于这两种风格,您都不能真正提出有效的论据-这确实是个人主观偏好的问题。


1)CERT-C DCL04-C

答案 12 :(得分:0)

考虑

int *x = new int();

这不会翻译为

int *x;
*x = new int();

它实际上翻译为

int *x;
x = new int();

这使int *x表示法有些不一致。