为什么大多数C程序员都将这样的变量命名为:
int *myVariable;
而不是像这样:
int* myVariable;
两者都有效。在我看来,星号是类型的一部分,而不是变量名称的一部分。任何人都可以解释这个逻辑吗?
答案 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 x
,int *const x
或int*const x
?
考虑int *const a, b;
,a
和b
的类型是什么? “恒星属于变量”不再那么明显了。相反,人们会开始思考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*
,所以星星属于类型限定符。
但是基本上我的结论是,这里发表的许多论点只是主观的和幼稚的。对于这两种风格,您都不能真正提出有效的论据-这确实是个人主观偏好的问题。
答案 12 :(得分:0)
考虑
int *x = new int();
这不会翻译为
int *x;
*x = new int();
它实际上翻译为
int *x;
x = new int();
这使int *x
表示法有些不一致。