来自C背景,我总是假设POD类型(例如整数)从未在C ++中自动进行零初始化,但似乎这是完全错误的!
我的理解是,只有'裸'非静态POD值不会被填零,如代码片段所示。我做对了,还有其他重要的案例我错过了吗?
static int a;
struct Foo { int a;};
void test()
{
int b;
Foo f;
int *c = new(int);
std::vector<int> d(1);
// At this point...
// a is zero
// f.a is zero
// *c is zero
// d[0] is zero
// ... BUT ... b is undefined
}
答案 0 :(得分:17)
假设您在调用a
之前未修改test()
,a
的值为零,因为具有静态存储持续时间的对象在程序启动时为零初始化。
d[0]
的值为零,因为std::vector<int> d(1)
调用的构造函数具有第二个参数,该参数采用默认参数;第二个参数被复制到正在构造的向量的所有元素中。默认参数为T()
,因此您的代码等同于:
std::vector<int> d(1, int());
b
具有不确定的值,这是正确的。
f.a
和*c
都有不确定的值。要初始化它们(对于POD类型与零初始化相同),您可以使用:
Foo f = Foo(); // You could also use Foo f((Foo()))
int* c = new int(); // Note the parentheses
答案 1 :(得分:1)
实际上某些值为零可能是由于您在应用程序的调试版本中尝试此代码(如果是这种情况)。
如果我没记错,请在你的代码中:
答案 2 :(得分:1)
请注意,操作系统作为安全功能进行的零初始化通常仅在第一次分配内存时完成。我指的是堆,堆栈和数据部分中的任何段。堆栈和数据部分通常具有固定大小,并在应用程序加载到内存时初始化。
数据段(包含静态/全局数据和代码)通常不会被“重用”,但如果在运行时动态加载代码则可能不是这种情况。
堆栈段中的内存会一直被重用。局部变量,函数堆栈帧等都经常被使用和重用,并且每次都没有初始化 - 就在首次加载应用程序时。
但是,当应用程序发出堆内存请求时,内存管理器通常会在授予请求之前对内存段进行零初始化,但仅针对新段。如果您请求堆内存,并且已经初始化的段中有可用空间,则不会再次执行初始化。因此,无法保证如果您的应用程序重用该特定内存段,它将再次进行零初始化。
所以,例如,如果你在堆上分配一个Foo,为它的字段赋值,删除Foo实例,然后在堆上创建一个新的Foo,新的Foo有可能被分配到与旧Foo相同的内存位置,因此其字段最初将具有与旧Foo字段相同的值。
如果您考虑一下,这是有道理的,因为操作系统只是初始化数据以防止一个应用程序访问另一个应用程序的数据。允许应用程序访问自己的数据的风险较小,因此出于性能原因,每次都不会进行初始化 - 只是第一次使特定的内存段可供应用程序使用(在任何段中)。
但是,有时在调试模式下运行应用程序时,某些调试模式运行时会在每次分配时初始化堆栈和堆数据(因此,您的Foo字段将始终初始化)。但是,不同的调试运行时将数据初始化为不同的值。有些零初始化,有些初始化为“标记”值。
关键是 - 永远不要在代码中的任何位置使用未初始化的值。绝对不能保证它们将被初始化为零。此外,请务必阅读之前关于parens和默认值与值初始化的链接文章,因为这会影响“未初始化”值的定义。
答案 3 :(得分:0)
对我来说,POD类型是根据它们所放置的内存部分进行初始化的。您的static int a
已在数据段上分配,因此它在启动时具有默认值。但是,我认为f在你的例子中没有被激活......
答案 4 :(得分:0)
他们没有。调试位版本可能会这样做,但通常它只是放入内存中,并初始化为内存中的值。