什么区别声明、定义和初始化变量?

102

阅读了这个问题后,我知道了声明和定义之间的区别。那么这是否意味着定义等于声明加初始化?


1
初始化是针对变量的。定义也可以适用于函数,其中您定义函数体。 - FKaria
所以你的意思是对于一个变量来说,是这样的。 - Tony
4个回答

123

声明

在程序中,声明通常指引入一个新的名称。例如,您可以通过描述其“签名”来声明一个新函数:

void xyz();

或者声明一个不完整的类型:

class klass;
struct ztruct;

最后但并非最不重要的,声明一个对象:

int x;

在C ++标准中,第3.1 / 1节描述了:

声明(第7条)可以将一个或多个名称引入翻译单元或重新声明先前声明的名称。

定义

定义是先前声明的名称的定义(或者它可以是定义和声明)。例如:

int x;
void xyz() {...}
class klass {...};
struct ztruct {...};
enum { x, y, z };

具体而言,C++标准在§3.1/1中定义如下:
声明是定义,除非它声明了一个函数但没有指定函数的主体(8.4),它包含了外部说明符(7.1.1)或链接说明符(7.5),既没有初始化程序也没有函数主体,在类定义中声明了一个静态数据成员(9.2、9.4),它是一个类名声明(9.1),是一个不透明枚举声明(7.2),是一个模板参数(14.1),是一个函数声明符中的参数声明(8.3.5),该函数声明符不是函数定义符,或者它是一个typedef声明(7.1.3),别名声明(7.1.3),使用声明(7.3.3),static_assert声明(Clause 7),属性声明(Clause 7),空声明(Clause 7)或使用指令(7.3.4)。

初始化

初始化是指在构造时“赋值”的过程。对于类型为T的通用对象,通常采用以下形式:
T x = i;

但在C++中,它可以是:

T x(i);

甚至还有:
T x {i};

使用C++11。

结论

那么定义是否等于声明加初始化呢?

这取决于你所讨论的内容。如果你讨论的是一个对象,例如:

int x;

这是一个没有初始化的定义。而下面这个则是一个有初始化的定义:

int x = 0;

在某些情况下,谈论“初始化”、“定义”和“声明”没有意义。例如,如果你正在讨论一个函数,那么“初始化”并没有太多意义。
因此,答案是“不”,定义并不自动意味着声明加初始化。

22
有些不准确:int x;既是定义也是声明。 - Angew is no longer proud of SO
3
@Angew,谢谢,我已经添加了更完整的定义 - Shoe
我认为OP在他的帖子中提到的问题很好地回答了定义和声明。但最初的问题是“那么它意味着定义等于声明加初始化吗?” - Tahlil
@Tahlil,我已经添加了结论。感谢你的提醒。 - Shoe
3
@Tony,我认为(如果我错了,请有人纠正我以便我学习)'extern int x' 只是一个声明,意味着定义在别处。而 'int x' 实际上是定义了它,尽管它被分配了随机的垃圾值,除非你像 'int x = 5' 这样特别初始化它。 - brat

60
声明表明“这个东西存在于某处”:
int foo();       // function
extern int bar;  // variable
struct T
{
   static int baz;  // static member variable
};

定义表示“这个东西存在于这里;为它分配内存”:
int foo() {}     // function
int bar;         // variable
int T::baz;      // static member variable

在定义对象时,初始化是可选的,它表示“这个东西的初始值在这里”:

int bar = 0;     // variable
int T::baz = 42; // static member variable

有时可以在声明时进行操作:
struct T
{
   static int baz = 42;
};

...但这会涉及到更复杂的功能。


非常好的解释,只是初始化比那复杂一些。(如果这么简单,那就不是C++了。)初始化包括静态生存期变量的零初始化、默认构造函数以及您所展示的内容。(为了增加混淆:在C语言中,初始化可以是第一次将变量赋值;例如在“取未初始化变量的值”的语句中。我不会感到惊讶,如果其中一些也滑入了C++中。) - James Kanze
哦,还有一些特殊情况,C++允许在声明中指定初始化。 - James Kanze
1
@JamesKanze:为了这些目的,决定保持超级简单。 - Lightness Races in Orbit
这没有意义,静态成员变量会分配内存。 - GT 77
我不是专家(顺便说一句,baz需要是constconstexpr,否则它将无法编译),但我认为编译器允许初始化而不需要定义,是因为该值可以在编译时求值的表达式中使用。所以如果你有类似int x = T::baz;的代码,编译器会对其进行搜索和替换,表达式变成int x = 42;。如果你将baz推入函数调用中,foo(T::baz);,那么你应该通过定义来分配内存给它。 - undefined

9
对于C语言,至少根据C11 6.7.5的规定:
声明指定了一组标识符的解释和属性。对于一个标识符的定义是该标识符的声明,并满足以下条件:
- 对于对象,引起为该对象保留存储空间; - 对于函数,包括函数体; - 对于枚举常量,是该标识符的(唯一)声明; - 对于typedef名称,是该标识符的第一个(或唯一的)声明。
而根据C11 6.7.9.8-10的规定:
初始化程序指定了存储在对象中的初始值……如果自动存储的对象没有被明确初始化,则其值是不确定的。
因此,广义地说,声明引入了标识符并提供有关信息。对于变量来说,定义是为该变量分配存储空间的声明。
初始化是指定要存储在对象中的初始值,这与您第一次显式赋值的时间不一定相同。当您定义变量时,它已经有了一个值,无论您是否显式给它赋值。如果您没有显式给它赋值,并且变量具有自动存储,则它将具有初始值,但该值将是不确定的。如果它具有静态存储,则会根据类型进行隐式初始化(例如,指针类型会初始化为null指针,算术类型会初始化为零等)。
因此,如果您定义一个自动变量而没有为其指定初始值,例如:
int myfunc(void) {
    int myvar;
    ...

您正在定义它(因此也在声明它,因为定义是声明),但未初始化它。因此,定义不等于声明加初始化。

在C++中,初始化有很大的不同。(C语言也有静态生存期对象的零初始化等效方法。) - James Kanze
@JamesKanze:是的,不幸的是这个问题被标记为C和C ++,在它们不同之处并没有什么帮助。 - Crowman
相当。对于兼容类型,在C++中的初衷(至少最初)是它们的行为与C中相同;实际上,我认为它们在语言本身兼容的所有情况下仍然表现得相同。但用于定义此行为的措辞却非常不同。 - James Kanze

1
“这是否意味着定义等于声明加上初始化?”
不一定,你的声明可能没有任何变量被初始化,例如:
 void helloWorld(); //declaration or Prototype.

 void helloWorld()
 {
    std::cout << "Hello World\n";
 } 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接