默认变量值

时间:2011-05-17 14:48:28

标签: c++ c

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

e.g。

float x;

9 个答案:

答案 0 :(得分:53)

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

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

零初始化 T类型的对象意味着:

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

默认初始化 ,T类型的对象意味着:
- 如果T是非POD类类型(第9节),则调用T的默认构造函数(和   如果T没有可访问的默认构造函数,则初始化是错误的;) - 如果T是数组类型,则每个元素都是默认初始化的;
- 否则,对象被零初始化。

value-initialize T类型的对象意味着:
- 如果T是具有用户声明的构造函数(12.1)的类类型(第9节),则为默认值   调用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;
}

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

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

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

答案 1 :(得分:16)

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

答案 2 :(得分:5)

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

答案 3 :(得分:5)

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

Default initialization is performed in some cases

  1. 在没有初始化程序的情况下声明具有自动,静态或线程本地存储持续时间的变量时;
  2. 通过不带初始化程序的new表达式创建具有动态存储持续时间的对象时;
  3. 当构造函数初始化列表中未提及基类或非静态数据成员时,将调用该构造函数。

和此默认初始化的影响是:

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

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

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

意味着,如果未初始化的变量是局部变量(例如,int仅出现在函数的作用域中),则其值是不确定的(未定义行为)。 cppreference strongly discourages the usage of uninitialized variables

请注意,即使大多数现代编译器在检测到未初始化的变量正在使用时,都会在编译时发出错误(通常在编译时),但是在您“诱骗”它们的情况下,它们通常不会这样做。认为您可能以某种方式初始化了变量,例如:

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

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

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

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

  • 如果使用类型为unsigned charstd::byte的不确定值来初始化另一个类型(可能为cv限定)的变量unsigned charstd::byte; < / p>

  • 如果来自

    的结果是unsigned char或std :: byte类型的不确定值(自C ++ 17起)
    • 条件表达式的第二或第三操作数,
    • 逗号运算符的右操作数
    • 强制转换或转换为unsigned charstd::byte(可能具有cv资格)的操作数,
    • 舍弃值表达式。

有关变量的默认初始化及其行为的其他详细信息,请参见here

为了更深入地探究不确定的价值,在2014年进行了以下更改(如Shafik Yaghmour pointed out here,并附有其他有用的资源):

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

收件人:

如果未为对象指定初始化程序,则该对象将被默认初始化。当获得具有自动或动态存储持续时间的对象的存储时,该对象具有不确定的值,并且如果未对该对象执行任何初始化,则该对象将保留不确定的值,直到替换该值为止。 [注意:具有静态或线程存储持续时间的对象被零初始化]如果评估产生不确定的值,则该行为是不确定的,除了以下情况:

  • 如果通过以下运算得出不确定的无符号窄字符类型值:

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

    • 逗号的右操作数

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

    • 舍弃值表达式,

      则运算结果为不确定值。

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

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

最后,在以下情况下执行zero-initialization的主题:

  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
}

答案 4 :(得分:4)

这取决于您声明它的位置。全局范围中的变量初始化为0,堆栈变量未定义。

答案 5 :(得分:2)

我认为这是未定义的。我认为一些编译器在调试模式下编译时会将其初始化为零。但它也可以成为记忆中已有的东西。基本上 - 不要依赖任何一种行为。

更新:根据评论 - 全局变量将被零初始化。局部变量将是任何东西。

回答你的第二个问题:

  

谢谢 - 接下来就是这样,是否有快捷方式为以下所有内容分配零?:float x1,x2,x3,x4,x5,y1,y2,y3,y4,y5

你可以做到

float x[5] = {0,0,0,0,0}; float y[5] = {0,0,0,0,0};

并使用x[0]代替x1

答案 6 :(得分:2)

它可以是编译器特定的,但通常发布版本不会将变量初始化为任何特定值,因此您可以获得内存中剩余的内容。某些编译器在调试版本中使用某些幻数来标记特定的内存区域。

答案 7 :(得分:2)

在初始化之前使用任何变量的值(请注意,静态存储持续时间对象始终初始化,因此这仅适用于自动存储持续时间)会导致未定义的行为。这与包含0作为初始值或包含随机值非常不同。 UB意味着可能会发生任何。在使用陷阱位的实现时,它可能会使程序崩溃或生成信号。多个读取也可能导致不同的不可预测的值,以及任何其他可想象的(或不可想象的)行为。只需不要使用未初始化变量的值

注意:根据评论编辑了以下内容:

请注意,除非您可以确保类型foo_t没有填充位,否则此类代码无效:

foo_t x;
int i;
for (i=0; i<N; i++) x = (x<<1) | get_bit();

尽管意图是最初在x中的“随机值”在循环结束之前被丢弃,但程序一旦访问x就可以调用UB来执行操作{{1在第一次迭代中,因此整个程序输出无效。

答案 8 :(得分:1)

C ++不会实例化变量。 x的值是当时在内存中发生的任何事情。永远不要假设它的初始值。