我经常在C和C ++代码中看到以下约定:

some_type val;
val = something;

some_type *ptr = NULL;
ptr = &something_else;


而不是

some_type val = something;
some_type *ptr = &something_else;


我最初是假设这是从必须在范围顶部声明所有局部变量的日子起遗留下来的习惯。但是我学会了不要这么快就消除资深开发人员的习惯。因此,是否有充分的理由在一行中声明并在事后分配?

评论

+1表示“我学会了不要这么快就摒弃资深开发人员的习惯。”这是一个明智的教训。

#1 楼

C

在C89中,所有声明都必须在范围的开头({ ... }),但是此要求很快就被放弃了(首先是编译器扩展,然后是标准)。 br /> C ++

这些示例并不相同。 some_type val = something;调用复制构造函数,而val = something;调用默认构造函数,然后调用operator=函数。这种差异通常很关键。

习惯

有些人更喜欢先声明变量,然后再定义它们,以防他们稍后使用一个声明重新格式化其代码。

关于指针,有些人只是习惯于初始化指向NULLnullptr的每个指针,无论他们使用该指针做什么。

评论


非常感谢C ++,谢谢。普通C呢?

–乔纳森·斯特林(Jonathan Sterling)
2011年5月7日20:35

MSVC仍然不支持声明,除非在以C模式进行编译时在块的开头,这对我来说是无休止的来源。

– Michael Burr
2011年5月7日22:03

@Michael Burr:这是因为MSVC完全不支持C99。

–orlp
2011年5月7日22:05

“ some_type val = something;调用副本构造函数”:它可以调用副本构造函数,但是标准允许编译器取消临时的默认构造,val的副本构造和临时的销毁,并使用a直接构造val some_type构造函数,将某些内容作为唯一参数。在C ++中,这是一个非常有趣且不寻常的边缘情况……这意味着对这些操作的语义有一个假设。

–托尼
2011年5月9日在9:15

@Aerovistae:对于内置类型,它们是相同的,但是对于用户定义类型,不一定总是说相同。

–orlp
2012-10-11 18:48

#2 楼

您已经同时为问题C和C ++加上了标签,而答案在这些语言中却大不相同。

首先,问题标题的措词不正确(或更准确地说,是不相关的)问题本身)。在您的两个示例中,都在一行中同时声明和定义了变量。您的示例之间的区别在于,在第一个示例中,变量要么未初始化,要么用虚拟值初始化,然后在以后为其分配有意义的值。在第二个示例中,变量立即被初始化。

其次,在C ++语言中,正如@nightcracker在其回答中指出的,这两个结构在语义上是不同的。第一个依赖于初始化,而第二个则依赖于分配。在C ++中,这些操作是可重载的,因此有可能导致不同的结果(尽管可以注意到,产生非等效的初始化和赋值重载不是一个好主意)。

在原始标准C语言中(C89 / 90)在块的中间声明变量是非法的,这就是为什么您可能会在块的开头看到声明为未初始化(或用伪值初始化)的变量,然后在以后分配有意义的值的原因值变为可用。

在C99语言中,可以在块的中间声明变量(就像在C ++中一样),这意味着仅在某些特定情况下,当初始化程序时才需要第一种方法在声明时未知。 (这也适用于C ++。)

评论


@乔纳森·斯特林:我读了你的例子。您可能需要复习C和C ++语言的标准术语。具体而言,在这些语言中具有特定含义的术语声明和定义。我再重复一遍:在您的两个示例中,变量都在一行中声明和定义。在C / C ++中,行some_type val;立即声明并定义变量val。这就是我在回答中的意思。

– AndreyT
2011年5月7日20:53

我明白你在那儿的意思。您绝对正确地声明和定义我使用它们的方式变得毫无意义。我希望您接受我对措辞不佳和评论不周到的歉意。

–乔纳森·斯特林(Jonathan Sterling)
2011年5月7日20:55

因此,如果一致认为“声明”是错误的词,我建议比我更了解该标准的人来编辑Wikibooks页面。

–乔纳森·斯特林(Jonathan Sterling)
2011年5月7日在21:11

在其他任何上下文中,声明都是正确的词,但是由于声明是一个定义明确的概念,因此在C和C ++中,您不能像在其他上下文中那样宽松地使用它。

–orlp
2011年5月7日21:15

@ybungalobill:你错了。 C / C ++中的声明和定义不是互斥的概念。实际上,定义只是声明的一种特定形式。每个定义都同时是一个声明(几乎没有例外)。有定义声明(即定义)和非定义声明。而且,通常情况下,始终使用therm声明(即使它是一个定义),除非上下文之间的区别至关重要。

– AndreyT
2011年5月7日21:52

#3 楼

我认为这是一个古老的习惯,是“本地声明”时代遗留下来的。因此,作为对您问题的回答:不,我不认为有充分的理由。我从来没有自己做过。

#4 楼

我在回答Helium3的问题时说了一些有关这一点。

基本上,我说这是很容易看到变化的视觉辅助。

if (a == 0) {
    struct whatever *myobject = 0;
    /* did `myobject` (the pointer) get assigned?
    ** or was it `*myobject` (the struct)? */
}




if (a == 0) {
    struct whatever *myobject;
    myobject = 0;
    /* `myobject` (the pointer) got assigned */
}


#5 楼

其他答案都很好。在C中有一些关于此的历史。在C ++中,构造函数和赋值运算符之间是有区别的。

从视觉上来说,在阅读代码时,更平凡的工件(例如变量的类型和名称)并不是您想要的。这是您通常最感兴趣的语句,花费大量时间盯着别人看,因此有一种趋势可以浏览其余的语句。

如果我有一些类型,名称和赋值都在进行在相同的狭小空间中,这会造成一些信息过载。此外,这意味着在我通常浏览的空间中发生了一些重要的事情。垂直空间可以使其更好。我认为这类似于为什么您不应该编写拥挤的行来在狭窄的垂直空间中执行大量的指针算术和赋值操作-仅仅是因为该语言让您摆脱了这些事情并不意味着您应该这样做一直如此。 :-)

#6 楼

在C语言中,这是标准做法,因为变量必须在函数开始时声明,而不像C ++中那样,可以在函数主体中的任何地方声明变量以供以后使用。指针被设置为0或NULL,因为它只是确保指针指向无垃圾。否则,我没有想到的重大优势,这迫使任何人都这样做。

#7 楼

本地化变量定义及其有意义的初始化的优点:


如果变量在代码中首次出现时便习惯性地分配了有意义的值(对同一件事的另一种看法:将它们的出现延迟到可以使用有意义的值),那么就不可能将它们无意中与无意义或未初始化的值一起使用(很容易发生,因为由于条件语句,短路评估,异常等原因而意外地跳过了一些初始化操作)

效率更高


避免设置初始值的开销(默认构造或初始化为诸如NULL之类的前哨值)

operator=有时可以效率较低并且需要临时对象

(尤其是内联函数),优化器可以消除一些/所有效率低下的问题>最小化变量的范围,反过来最小化平均浓度的变量当前作用域:此


使您更容易从精神上跟踪作用域中的变量,可能影响这些变量的执行流和语句以及它们的值导入至少对于某些复杂且不透明的对象而言,这会减少程序的资源使用量(堆,线程,共享内存,描述符)


有时更简洁,因为您不在其中重复变量名定义,然后进行初始有意义的赋值
,对于某些类型(例如引用)以及当您希望对象为const时是必需的

有时可以方便和/或简洁地排除多个变量的类型:类型名称过长或过于复杂,有时可以使用the_same_type v1, v2, v3;更好)


有时候,最好将变量与变量的使用无关地进行分组,以强调某些操作中涉及的变量(和类型)的集合:类型的通用性并使其更容易更改,同时仍坚持每行的变量,从而便于复制粘贴,typedef注释等。


通常是这种情况在编程中,虽然在大多数情况下一个实践可以带来明显的经验利益,但在少数情况下,另一种实践确实可以取得压倒性的优势。

评论


我希望有更多的语言能够区分代码声明和设置变量值的情况,尽管新变量可以使用相同的名称,但该变量永远不会在其他地方编写[无论后面的语句使用相同的变量还是使用不同的语句,其行为都将相同。尽管两个用例将以相同的方式执行,但是在尝试查找错误时,了解变量何时可能更改非常有用。

–超级猫
2014年4月28日在22:50