本地变量和全局变量默认如何初始化?

12

根据以下的内容,我是否正确?

  • global_A引用被初始化为null。
  • global_int为0。
  • local_A引用为null。
  • local_int未初始化。
  • 全局变量global_A.x和局部变量local_A.x均未初始化。

谢谢任何帮助。


A global_A;
int global_int;

class A {
  public : int x;
}

int main()
{
  int local_int;
  A local_A;
}

3
他是新来的,请不要太苛刻。 - Chad
你还需要明确“在哪个时刻”你谈论这些值,一般来说(在进入main之前,在main的开括号等)。 - Chubsdad
1
很高兴你提出这个问题;阅读答案后,似乎有很多人对此感到困惑。 - tenfour
一些编译器会在你设置一个标志请求或者启用调试编译时,对变量进行零初始化。 - Fuzz
10个回答

14
在Andrey的回答上进行扩展。 $3.6.2-“具有静态存储期(3.7.1)的对象必须在进行任何其他初始化之前进行零初始化(8.5)。 ”在OP中,“global_A”和“global_int”具有静态存储期。 “local_int”和“local_A”没有链接,因为它们是本地对象。 $8.5/5-将类型T的对象进行零初始化意味着:
—如果T是标量类型(3.9),则将对象设置为值为0(零)转换为T的值;
—如果T是非联合类类型,则每个非静态数据成员和每个基类子对象都将被零初始化;
—如果T是联合类型,则object的第一个命名数据成员89)值初始化;
—如果T是数组类型,则每个元素均为零初始化;
—如果T是引用类型,则不执行初始化。 $6.7.4/4-“所有具有静态存储期(3.7.1)的本地对象的零初始化(8.5)在进行任何其他初始化之前执行。用常量表达式初始化的带有静态存储期的POD类型(3.9)的本地对象将在首次进入其块之前初始化。实现允许在与允许在名称空间范围内静态初始化具有静态存储期的对象的条件下提早初始化具有静态存储期的其他本地对象(3.6.2)。否则,这样的对象将在控制通过其声明的第一次初始化时初始化;完成其初始化后,将认为此类对象已初始化。如果初始化通过抛出异常而退出,则初始化未完成,因此将在下一次控制进入声明时再次尝试。如果在初始化对象时递归地重新进入声明(递归),则行为未定义。”
编辑2: $8.5/9-“如果未为对象指定初始化程序,并且该对象是(可能为cv限定)的非POD类类型(或其数组),则该对象应默认初始化;如果该对象是const-qualified类型,则底层类类型必须具有用户声明的默认构造函数。 否则,如果未为非静态对象指定初始化程序,则对象及其子对象(如果有)具有不确定的初始值90);如果对象或其任何子对象是const-qualified类型,则程序无法形成。”
一般来说,您需要阅读这些部分以及$8.5,以更好地掌握此方面知识。

7

你的代码中没有引用,所以任何提到“引用”的地方都没有意义。

在你的示例中,全局对象global_intglobal_A都被初始化为零。两个本地对象local_intlocal_A包含不确定的值,这意味着local_intlocal_A.x未被初始化。

P.S. 当然,正如其他人已经指出的那样,你的代码无法编译。你不能在声明class A之前声明A对象(并且你忘记了在类定义后加上;)。


7

基本上,每当您声明一个变量时,编译器都会调用其默认构造函数,除非您另行指定。

语言级别的类型(例如指针、'int'、'float'、'bool'等)的“默认构造函数”实际上什么也不做,它只是在声明时将内存保持原样(全局/静态变量是特殊情况,请参考chubsdad's answer了解更多细节)。这意味着它们可以是任何东西,因为通常无法确定该内存之前包含什么内容,甚至无法确定内存来自哪里(除了“放置new”运算符的情况)。

您创建的类没有构造函数,因此编译器将为您生成一个默认构造函数,它只是简单地调用其成员/变量的构造函数。如果结合前一段提供的信息,您可以看到变量“x”将调用其默认构造函数,什么也不做,因此未初始化为任何值。

正如其他人所说,您的代码中没有引用或指针,因此在这里所有情况下都无效的术语“NULL”。NULL通常是指一个指针,就像其他语言级别的类型一样,在分配值之前不会设置任何内容(除非它是全局/静态变量)。

2
默认构造函数不执行任何操作的说法是误导性的,因为显式调用它实际上会将值设置为相应的“null”值(int a = int();a 初始化为零)。我想更直观地说,默认构造函数不会自动调用原始类型,这些类型保持未初始化状态。 - Ferdinand Beyer
1
有时,原始类型的“默认构造函数”也会被自动调用。一个这样的例子是: struct { int x; float f; } s = { 9 }; assert(s.x == 9); assert(s.f == 0.0f); - Dennis Zickefoose
实际上,问题在于称其为默认构造函数。 - Dennis Zickefoose
1
@Dennis @Ferdinand:好观点,尽管我更喜欢这种方式考虑它,因为它提供了一个变量初始化的一致模型。我可能错过了一些边缘情况,但在我看来,这可能是解释给“新手”的最佳方式,因为遵循它不应该导致任何问题(即假设未初始化时已经初始化,而不是假设未初始化时未初始化),而且它与事实也不太远。 - Grant Peters

3

为了完整起见,如果您有参考资料:

引用必须在声明时初始化,否则编译器会报错。这意味着引用总是需要另一个值或引用来引用它(就像上面所说的),编译器会确保您不会忘记它。这也意味着引用永远不可能是空指针。但是,它们所引用的对象可能变得无效。


1
A global_A;

这是一个实例,而不是指针,你的程序将在进入主函数之前调用构造函数。

要获取一个指向实例而不是实例本身的指针,你需要编写:

A* global_A;

全局变量global_int被初始化为0,因为所有的全局变量都会被初始化为它们的默认值。

变量A local_A将在每次程序进入声明它的函数时通过调用其构造函数进行初始化。

与之前一样,如果你想要一个指向A的指针,你必须写A *local_A,但这次你必须自己将其初始化为NULL。

A *local_A = NULL;

变量local_int不会被初始化,因为它是一个原始类型。

如果local_A.x被初始化取决于A的构造函数,如果默认构造函数不会初始化local_A.x。如果x是一个类实例,则创建A的实例将使用其类的构造函数初始化x。


至少在gcc中,global_int将被初始化为0,因为它是全局的。 - jbernadas
1
@jbernadas 你是对的,这是语言规范的一部分。已经修复了。 - josefx

1

global_A和local_A不是引用,它们是对象,并使用它们的默认构造函数创建。默认构造函数未指定,因此将生成它,它将不执行任何操作,因此成员变量将保持未初始化状态。


1
那么对于全局变量 global_A 和局部变量 local_A 都创建了对象吗?抱歉我混淆了 Java 和 C++。当我这里说引用时,我指的是地址。那么这些变量是否指向任何对象呢? - extdummy
C++与Java不同,它既有对象,也有指向对象的指针。你声明的是对象,而不是指针。你似乎认为代码的语法是A* global_A = NULL; - dan04

-2

嗨,大家好。我看到这里的回复感到更加困惑了。不管怎样,我进行了如下测试:

1 #include

  2 using namespace std;
  3 
  4 class A {
  5 
  6 public :
  7         A() : x(9) {};
  8         int x;
  9 
 10 };
 11 
 12 A global_a;
 13 int global_b;
 14 
 15 int main() {
 16 
 17         A local_a;
 18         int local_b;
 19         cout << "global_a.x = " << global_a.x << '\n';
 20         cout << "local_a.x = " << local_a.x << '\n';
 21 
 22         cout << "global_b = " << global_b << '\n';
 23         cout << "local_b = " << local_b << '\n';
 24 
 25 }

使用我的g++编译器在Ubuntu Linux上的结果:

global_a.x = 9

local_a.x = 9

global_b = 0

local_b = 0

我认为local_b应该是未定义的,但不知何故编译器默认初始化了它。然而,对于local_a...我不确定是否应该默认初始化。从这里的测试来看,local_a似乎已经被初始化了。不确定是否符合标准C++规范(例如,《C++ Primer》第4版说无论类变量在哪里声明,都会使用默认构造函数 - 这是否意味着类类型的变量无论是全局还是局部都会被初始化?)。

无论如何...这是一个非常混乱的问题。也许我应该放弃学习C++。Java要简单得多。是的!


添加默认构造函数会改变A实例的初始化方式。现在,global_alocal_a都由默认构造函数初始化。至于local_b,它只是恰好为0。虽然它确实未初始化,但这不是您可以通过编写程序测试的内容,因为如果内存位置中有0,未初始化的变量很容易“恰好”为0。 - John Kugelman
关于 A 的令人困惑之处在于,如果您没有提供构造函数,那么编译器会提供一个默认构造函数。但是这个默认构造函数什么也不做!它是一个空操作。它以其默认值保留了 x 字段:对于 global_a,这意味着 x 具有其默认值 0,而对于 local_a,这意味着 x 未初始化,因为其默认状态是未初始化的。 - John Kugelman

-2

它们都需要被初始化。编译器会对此发出警告。


2
全局变量被初始化为0。 - John Kugelman
2
全局变量不会被初始化为零。 - Chad
1
@Chad:在C++中,所有具有静态存储期限制的对象(包括所谓的“全局变量”)在任何其他初始化开始之前都会被始终零初始化 - AnT stands with Russia
@extdummy 不用担心。投票将会筛选出正确的答案或答案集。只需给它一些时间,让投票结果稳定下来即可。 - John Kugelman
@AndreyT:没错。据我所知,只有非POD类型才会被初始化为0。 - jweyrich
显示剩余5条评论

-2

除非你提前声明A,否则这段代码无法编译。

global_A引用被初始化为null - 不是,它将引用一个A对象。 global_int为0 - 应该是,需要检查一下。 local_A引用为null - 不是,与global_A相同。 local_int未初始化 - 是的,它将得到一些垃圾值。 全局变量global_A.x和局部变量local_A.x都未初始化 - 是的。

你总是可以调试并自行查看。


这也是我理解的,但问题在于当我打印global_A或local_A引用时,什么都没有。看起来它是空值,因为没有任何输出。 - extdummy
你要如何准确地打印它们? - Arjor

-3

global_A引用被初始化为null。

不是的,它是一个有效的对象(基于默认构造函数构建,虽然您的代码中没有,但编译器会添加)。

global_int为0。

是的。

local_A引用为null。

不是的,原因与全局变量相同。

local_int未初始化。

不是的,它被初始化为0。

全局变量global_A.x和局部变量local_A.x都未初始化。

不是的,两者都被初始化为0。


3
本地变量未初始化。 - John Kugelman
1
@John:local_A 通过调用其默认构造函数进行初始化。 - Ben Voigt

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