默认变量值

Cap*_*rog 51 c++

如果我在声明它时没有为变量赋值,它是默认为零还是只是以前在内存中的任何内容?

例如

float x;
Run Code Online (Sandbox Code Playgroud)

Alo*_*ave 55

声明的变量可以是零初始化,值初始化默认初始化.

C++ 03 Standard 8.5/5恰当地定义了每个:

零初始化的类型T指的对象:

- 如果T是标量类型(3.9),则将对象设置为0(零)转换为T的值;
- 如果T是非联合类类型,则每个非静态数据成员和每个基类子对象
都是零初始化的;
- 如果T是联合类型,则对象的第一个命名数据成员为零初始化;
- 如果T是数组类型,则每个元素都是零初始化的;
- 如果T是引用类型,则不执行初始化.

缺省初始化类型T的对象是指:
-如果T是一个非POD类型(第9节),T的默认构造函数被调用(并形成不良的初始化如果T没有可访问的缺省的构造);
- 如果T是数组类型,则每个元素都是默认初始化的;
- 否则,对象被零初始化.

值初始化类型的物体T是指:
-如果T是一个类型(第9节)与用户声明的构造(12.1),然后对T中的默认构造函数被调用(以及初始化是形成不良的如果T没有可访问的默认构造函数);
- 如果T是没有用户声明的构造函数的非联合类类型,则T的每个非静态数据成员和基类组件都是值初始化的;
- 如果T是数组类型,则每个元素都是值初始化的;
- 否则,对象被零初始化

例如:

#include<iostream>
using namespace std;

static int a; //Zero Initialized
int b; //Zero Initialized

int main()
{
    int i;  //Undefined Behavior, Might be Initialized to anything
    static int j; //Zero Initialized

    cout<<"\nLocal Uninitialized int variable [i]"<<i<<"\n";

    cout<<"\nLocal Uninitialized Static int variable [j]"<<j<<"\n";

    cout<<"\nGlobal Uninitialized Static int variable [a]"<<a<<"\n";

    cout<<"\nGlobal Uninitialized int variable [b]"<<b<<"\n";

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

您会注意到变量的结果i在不同的编译器上会有所不同.应该使用这种本地未初始化的变量.实际上,如果打开严格的编译器警告,编译器应报告有关它的错误.以下是键盘报告错误的方法.

cc1plus: warnings being treated as errors
In function 'int main()':
Line 11: warning: 'i' is used uninitialized in this function
Run Code Online (Sandbox Code Playgroud)

编辑:正如@Kirill V. Lyadvinsky在评论中正确指出的那样,不应该是一个非常强大的词,并且可以使用完全有效的代码,可以使用未初始化的变量,因为他在评论中指出了一个例子.所以,我应该说:
除非你确切知道自己在做什么,否则你永远不应该使用未初始化的变量.

  • @Konrad:实际上,它*会造成伤害.读取未初始化的局部变量并没有给你一些垃圾值 - 它是**未定义的行为**.有些运行时环境会在读取未初始化的局部变量时关闭程序(Visual Studio Debug Builds是一个突出的例子). (10认同)
  • "不应该使用本地未初始化的变量" - 除非您播种随机数生成器并使用未初始化的内存作为增加的熵源.;-) (3认同)
  • @Konrad:不,即便如此.它不能保证是未初始化的内存 - 如果需要,编译器可以将其清零.因此,除非你想要额外的熵源可能不是一个,否则你不应该. (3认同)
  • 使用未定义的值是UB.**不应该**是完全正确的.Kirill的评论几乎是正确的 - 他的代码是有效的(并且,是的,编译器可能错误地警告它),但这只是因为他实际上并没有使用未初始化的值. (3认同)
  • "不应该被使用"是一个强有力的声明,因为我们知道所有强有力的陈述都是错误的:)如果您通过引用将未初始化的变量传递给函数,编译器会给出相同的警告,但这将是绝对正确的代码例如:`int x; const bool ok = get_value(x); if(ok){/*使用x*/}` (2认同)

Kir*_*sky 17

这取决于.如果这是一个局部变量(具有自动存储持续时间的对象),它将是未初始化的,如果它是一个全局变量(具有静态存储持续时间的对象),它将被初始化为零.检查这个答案.

  • 我认为最好使用`static vs. automatic`而不是`global vs. local`术语 (5认同)
  • 它还取决于类型 - 类类型的变量具有其默认构造函数,并且可以执行任何操作(包括任何内容). (2认同)

kus*_*sma 6

这取决于你在哪里声明它。全局范围内的变量初始化为 0,堆栈变量未定义。

  • 就像我对@Kirill 的评论一样,最好说“静态/自动”而不是“全局/本地”。如您所知,局部变量也可以是静态的 (2认同)

Jam*_*nze 6

它取决于变量的生命周期.具有静态生命周期的变量在程序启动之前始终为零初始化:基本类型,enums和指针的零初始化与您分配0(适当地转换为类型)的初始化相同.在调用构造函数之前,即使变量具有构造函数,也会发生这种情况.


小智 5

由于当前的最佳答案是在 2011 年编写的,并且仅涉及 C++03,因此我提供了一个更新的答案,以考虑 C++11 之后所做的更改。请注意,我正在剥离仅在 C++03 或 C++11 之前有效的任何信息以及可以在原始来源中看到的不必要的注释。我尽可能多地引用原始规格,以避免不必要的重新表述,这可能会导致信息不准确。如果您有兴趣深入研究某个主题,请查阅我提供的原始资料。另外,请注意,我主要关注关于 * 默认初始化 * 未定义行为 * 零初始化的规则 因为在我看来,这些是理解变量“默认”如何表现所需的最重要方面,因为问题是问。

在某些情况下执行默认初始化

  1. 当没有初始化程序声明具有自动、静态或线程本地存储持续时间的变量时;
  2. 当具有动态存储期的对象由没有初始化器的 new 表达式创建时;
  3. 当构造函数初始值设定项列表中未提及基类或非静态数据成员并且调用该构造函数时。

这个默认初始化的效果是:

  • 如果 T 是非 POD(直到 C++11)类类型,则考虑构造函数并针对空参数列表进行重载决议。调用选定的构造函数(默认构造函数之一)为新对象提供初始值;

  • 如果 T 是数组类型,则数组的每个元素都是默认初始化的;

  • 否则,什么都不做:具有自动存储持续时间的对象(及其子对象)被初始化为不确定的值。

这意味着如果未初始化的变量是局部变量(例如,int仅存在于函数作用域中),则其值是不确定的(未定义行为)。cppreference 强烈反对使用未初始化的变量

作为旁注,即使大多数现代编译器在检测到正在使用未初始化的变量时都会发出错误(在编译时),但如果您“欺骗”他们认为您可能以某种方式初始化变量,例如:

int main()
{
    int myVariable;
    myFunction(myVariable);  // does not change the variable
    cout << myVariable << endl;  // compilers might think it is now initialized
}
Run Code Online (Sandbox Code Playgroud)

从 C++14 开始,以下内容成立(注意std::byte是在 C++17 中引入的):

使用通过默认初始化任何类型的非类变量获得的不确定值是未定义行为(特别是,它可能是陷阱表示),但以下情况除外:

  • 如果类型的不确定值unsigned charorstd::byte被分配给另一个类型的变量(可能是 cv 限定的)unsigned charstd::byte(变量的值变得不确定,但行为不是未定义的);

  • 如果类型的不确定值unsigned charorstd::byte用于初始化另一个类型的变量(可能是 cv 限定的)unsigned charstd::byte

  • 如果 unsigned char 或 std::byte (C++17 起)类型的不确定值来自

    • 条件表达式的第二个或第三个操作数,
    • 逗号运算符的右操作数,
    • 强制转换或转换为(可能是 cv 限定的)unsigned char或的操作数std::byte
    • 丢弃值表达式。

可以在此处找到有关变量默认初始化及其行为的其他详细信息。

为了更深入地研究不确定值,2014 年进行了以下更改(正如Shafik Yaghmour 在此处指出的其他有用资源):

如果没有为对象指定初始化程序,则该对象是默认初始化的;如果不进行初始化,具有自动或动态存储持续时间的对象具有不确定的值。[注意:具有静态或线程存储持续时间的对象是零初始化的]

到:

如果没有为对象指定初始化程序,则该对象是默认初始化的。获取自动或动态存储期限的对象的存储时,该对象具有不确定值,如果未对该对象进行初始化,则该对象将保留不确定值,直到该值被替换。[注意:具有静态或线程存储持续时间的对象是零初始化的] 如果评估产生不确定值,则行为未定义,但以下情况除外:

  • 如果无符号窄字符类型的不确定值是通过评估产生的:

    • 条件表达式 (5.16 [expr.cond]) 的第二个或第三个操作数,

    • 逗号的右操作数,

    • 强制转换或转换为无符号窄字符类型的操作数,或

    • 丢弃值表达式,

      那么操作的结果是一个不确定的值。

  • 如果通过对第一个操作数是无符号窄字符类型的左值的简单赋值运算符的右操作数求值产生了无符号窄字符类型的不确定值,则不确定值将替换左操作数引用的对象的值.

  • 如果在初始化无符号窄字符类型的对象时通过对初始化表达式的求值产生无符号窄字符类型 (3.9.1 [basic.fundamental]) 的不确定值,则该对象将被初始化为不确定值。

最后,还有在以下情况下执行的零初始化主题:

  1. 对于每个具有静态或线程本地存储持续时间且不受常量初始化(自 C++14 起)的命名变量,在任何其他初始化之前。

  2. 作为非类类型和没有构造函数的值初始化类类型成员的值初始化序列的一部分,包括未提供初始化器的聚合元素的值初始化。

  3. 当使用太短的字符串文字初始化任何字符类型的数组时,数组的其余部分将被零初始化。

零初始化的效果是:

  • 如果 T 是标量类型,则对象的初始值是显式转换为 T 的整数常量零。

  • 如果 T 是非联合类类型,则所有基类和非静态数据成员都初始化为零,并且所有填充都初始化为零位。构造函数(如果有)将被忽略。

  • 如果 T 是联合类型,则第一个非静态命名数据成员被零初始化,所有填充都被初始化为零位。

  • 如果 T 是数组类型,则每个元素都初始化为零

  • 如果 T 是引用类型,则什么都不做。

以下是一些示例:

#include <iostream>
#include <string>

struct Coordinates {
    float x, y;
};

class WithDefaultConstructor {
    std::string s;
}

class WithCustomConstructor {
    int a, b;

public:
    WithCustomConstructor() : a(2) {}
}

int main()
{
    int a;    // Indeterminate value (non-class)

    int& b;   // Error

    std::string myString;    // Zero-initialized to indeterminate value
                             // but then default-initialized to ""
                             // (class, calls default constructor)

    double coordsArray[2];   // Both will be 0.0 (zero-initialization)

    Coordinates* pCoords;    // Zero-initialized to nullptr

    Coordinates coords = Coordinates();

    // x: 0.0
    // y: 0.0
    std::cout << "x: " << coords.x << '\n'
        "y: " << coords.y << std::endl;

    std::cout << a.a << a.b << a.c << '\n';

    WithDefaultConstructor wdc;    // Since no constructor is provided,
                                   // calls the default constructor

    WithCustomConstructor wcs;     // Calls the provided constructor
                                   // a is initialized, while b is
                                   // default-initialized to an indeterminate value
}
Run Code Online (Sandbox Code Playgroud)