Answers:
它们完全等效。但是,在
int *myVariable, myVariable2;
显而易见,myVariable的类型为int *,而myVariable2的类型为int。在
int* myVariable, myVariable2;
看起来两个都是int *类型,但这似乎不正确,因为intmyVariable2
类型是不正确的。
因此,第一种编程风格更加直观。
int* someVar
个人项目。这更有意义。
int[10] x
。这根本不是C的语法。语法明确地将解析为:int (*x)
,而不是(int *) x
,因此将星号放在左侧只会产生误导,并且是基于对C声明语法的误解。
int* myVariable; int myVariable2;
。
如果换一种方式来看,它*myVariable
是type int
,这很有意义。
myVariable
可以为NULL,在这种情况下*myVariable
会导致分段错误,但是不存在NULL类型。
int x = 5; int *pointer = &x;
,因为这表明我们将int设置*pointer
为某个值,而不是pointer
本身。
因为该行中的*与变量的绑定比与类型的绑定更紧密:
int* varA, varB; // This is misleading
正如@Lundin在下面指出的那样,const增加了更多需要考虑的细节。您可以通过声明每行一个变量来完全避免这种情况,这永远不会模棱两可:
int* varA;
int varB;
清晰代码和简洁代码之间的平衡很难实现-十几行多余的代码int a;
也不是很好。尽管如此,我还是默认为每行一个声明,并担心稍后再组合代码。
int *const a, b;
。*“绑定”在哪里?类型a
是int* const
,那么当*是类型本身的一部分时,您怎么能说*属于该变量呢?
到目前为止,这里没有人提到的是该星号实际上是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标准仅对*
操作员定义了两种含义:
而间接只是一个单一的意义,没有多余的意义来声明一个指针中,只有间接的,这是提领操作做什么,它执行的间接访问,所以还言内等int *a;
,这是一种间接访问(*
手段间接访问),因此上面的第二个语句比第一个语句更接近标准。
int a, *b, (*c)();
“”声明为“将以下对象声明为int
:对象a
,对象所指向b
的对象以及从函数所指向的对象所返回的对象c
”。
*
在int *a;
不操作,并且它不取消引用a
(其甚至没有定义还)
*
(它仅提供了含义的完整表达,而不是在*
表达式中!)。它说“ int a;” 声明一个指针,它确实做了,但从未声明过,但是没有给出任何含义*
,将其读为* in的取消引用值a
仍然是int仍然完全有效,因为它具有相同的事实含义。没什么,真的没有什么可以与6.7.6.1中的语句相矛盾。
int *a;
是声明,而不是表达式。a
没有被取消引用int *a;
。 a
甚至还不存在*
。您是否认为int *a = NULL;
是因为它取消引用了空指针而导致错误?
我将在这里走出去,说这个问题有一个直接的答案,无论是对于变量声明还是对于参数和返回类型,这都应在名称旁边加上星号:int *myVariable;
。要了解原因,请查看如何在C中声明其他类型的符号:
int my_function(int arg);
为了功能
float my_array[3]
用于数组。
通用模式(在use之后称为声明)是将符号的类型分为名称之前的部分,名称周围的部分以及名称周围的这些部分,这些模仿了您用来获取符号的语法。左侧类型的值:
int a_return_value = my_function(729);
float an_element = my_array[2];
和:int copy_of_value = *myVariable;
。
C ++在使用引用的过程中抛出了一个扳手,因为使用引用时的语法与值类型的语法相同,因此您可以争辩说C ++对C采用了不同的方法。另一方面,C ++保留了相同的方法。 C在指针情况下的行为,因此在这方面引用确实是奇怪的。
这只是一个偏好问题。
当您阅读代码时,在第二种情况下更容易区分变量和指针,但是当您将同一类型的变量和指针放在一行中时,这可能会引起混淆(项目指南通常不建议这样做,因为会降低可读性)。
我更喜欢在类型名称旁边声明带有相应符号的指针,例如
int* pMyPointer;
一位伟大的大师曾经说过:“必须以编译器的方式阅读它。”
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
当然,这是关于const放置的主题,但此处适用相同的规则。
编译器将其读取为:
int (*a);
不作为:
(int*) a;
如果您习惯于将星形放置在变量旁边,这将使您的声明更易于阅读。它还可以避免出现如下烦人的现象:
int* a[10];
-编辑-
确切地解释我说的“解析为” int (*a)
时的含义,这意味着*
绑定a
比绑定更紧密int
,很大程度上是由于优先级更高,表达式中的4 + 3 * 7
3
绑定7
比绑定更紧密。4
*
对于ASCII技术的道歉,用于解析的AST简介int *a
大致如下:
Declaration
/ \
/ \
Declaration- Init-
Secifiers Declarator-
| List
| |
| ...
"int" |
Declarator
/ \
/ ...
Pointer \
| Identifier
| |
"*" |
"a"
如已清楚显示的那样,由于它们的共同祖先是,因此*
绑定更紧密,而您需要一直向上走到树上,以找到涉及的共同祖先。a
Declarator
Declaration
int
(int*) a
。
int
在这种情况下。步骤2.阅读声明,包括任何类型的修饰。 *a
在这种情况下。步骤3读取下一个字符。如果是逗号,请使用它,然后返回到步骤2。如果还有其他抛出语法错误。...
int* a, b;
并获得一对指针。我要说的是*
绑定到变量并被解析,而不是用于形成声明的“基本类型”的类型。这也是引入typedef来允许typedef int *iptr;
iptr a, b;
创建几个指针的部分原因。通过使用typedef,您可以将绑定*
到int
。
Declaration-Specifier
与中的“装饰” “组合” Declarator
以得出每个变量的最终类型。但是,它不会将装饰“移动”到声明说明符,否则int a[10], b;
将产生完全可笑的结果,它将解析int *a, b[10];
为int
*a
,
b[10]
;
。没有其他方法可以描述它了。
int*a
最终由编译器读取的a
类型为:“ 具有类型int*
”。这就是我最初的评论的意思。
因为当您有如下声明时,它更有意义:
int *a, *b;
int* a, b;
由b
一个指针int
。但是他们没有。并且有充分的理由。在您建议的系统下b
,以下声明的类型是什么int* a[10], b;
?
对于在一行中声明多个指针,我更喜欢用int* a, * b;
更直观的方式将“ a”声明为指向整数的指针,并且在声明“ b”时不混合样式。就像有人说的那样,我不会在同一条语句中声明两种不同的类型。
偏爱的int* x;
人试图将其代码强制进入虚构的世界,在虚构的世界中,类型在左侧,标识符(名称)在右侧。
我说“虚构”是因为:
在C和C ++中,通常情况下,声明的标识符被类型信息包围。
这听起来很疯狂,但是您知道这是真的。这里有些例子:
int main(int argc, char *argv[])
表示“ main
是一个函数,该函数接受int
和的指针数组char
并返回int
。” 换句话说,大多数类型信息在右侧。有些人认为函数声明不算什么,因为它们在某种程度上是“特殊的”。好,让我们尝试一个变量。
void (*fn)(int)
Mean 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 ++中,语法变得有点复杂(带有类,引用,模板,枚举类),并且作为对这种复杂性的反应,您会发现在许多声明中要花更多的精力将类型与标识符分离。换句话说,int* p
如果您检查了大量的C ++代码,则可能会看到更多的-style声明。
无论使用哪种语言,都可以始终在变量的左侧输入类型声明:(1)永不在同typedef
一条语句中声明多个变量;(2)使用s(或别名声明,具有讽刺意味的是,将别名放在类型左侧的标识符)。例如:
typedef int array_of_10_ints[10];
array_of_10_ints a;
(*fn)
使指针与fn
而不是返回类型相关联。
本主题中的许多参数都是主观的,而“星号绑定到变量名”的参数是幼稚的。这里有一些不只是观点的论点:
被遗忘的指针类型限定符
正式地,“星号”既不属于类型也不属于变量名,它是其自身的语法项目(称为指针)的一部分。正式的C语法(ISO 9899:2018)为:
(6.7)声明:
声明说明符 init-declarator-list opt;
其中的声明说明符包含类型(和存储),而init-declarator-list包含指针和变量名。如果进一步剖析该声明符列表语法,可以看到以下内容:
(6.7.6)声明符:
指针opt直接声明 符
...
(6.7.6)指针:
*
类型限定符列表opt
*
类型限定符列表opt 指针
其中声明符是整个声明,直接声明符是标识符(变量名),指针是星号,后跟属于指针本身的可选类型限定符列表。
使有关“星星属于变量”的各种样式参数不一致的原因是,它们忘记了这些指针类型限定符。int* const x
,int *const x
或int*const x
?
考虑一下int *const a, b;
,a
和的类型是b
什么?“恒星属于变量”不再那么明显了。相反,人们会开始思考const
归属地。
您可以肯定地说出星形属于指针类型限定符,但除此之外没有太多。
指针的类型限定符列表可能会给使用int *a
样式的人带来问题。那些在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。每个声明不要声明多个变量
刚读英语,然后常识同意一个声明应该是一个声明。
变量是否为指针都没有关系。在一行中声明每个变量几乎可以使代码更清晰。
因此,关于程序员感到困惑的争论int* a, b
是不好的。问题的根源是使用多个声明符,而不是的放置*
。无论样式如何,您都应该编写以下代码:
int* a; // or int *a
int b;
另一个合理但主观的论点是,给定int* a
类型a
毫无疑问int*
,因此恒星属于类型限定符。
但基本上我的结论是,此处发表的许多论点只是主观的和幼稚的。对于这两种风格,您都无法真正提出有效的论据-这确实是个人主观偏好的问题。
1) CERT-C DCL04-C。
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
正如伟大的先知丹·萨克斯(Dan Saks)所教导的:“如果您始终将其const
尽可能地靠右放置,而又不改变其语义,”这将完全停止。成为一个问题。这也使您的const
声明更一致阅读。“ const单词右边的所有内容都是const,左边的所有内容都是其类型。” 这将适用于声明中的所有const。试试int const * * const x;
考虑
int *x = new int();
这不会转化为
int *x;
*x = new int();
它实际上转化为
int *x;
x = new int();
这使int *x
表示法有些不一致。
new
是C ++运算符。他的问题被标记为“ C”而不是“ C ++”。
typedefs
,但这会增加不必要的复杂性,恕我直言。