为什么文件作用域静态变量必须初始化为零
Why file scope static variables have to be zero-initialized?
C++默认初始化不使用自动存储将变量清零,为什么要对静态存储变量进行特殊处理?
它是由C和C++定义的东西吗?如果是这种情况,为什么C决定进行零初始化?
如果为文件范围静态变量提供初始值设定项,则它们将首先进行零初始化,然后再次进行常量/动态初始化。这不是多余的吗?例如,以下代码来自cppreference:http://en.cppreference.com/w/cpp/language/zero_initialization
#include <string>
double f[3]; // zero-initialized to three 0.0's
int* p; // zero-initialized to null pointer value
std::string s; // zero-initialized to indeterminate value
// then default-initialized to ""
int main(int argc, char* argv[])
{
static int n = argc; // zero-initialized to 0
// then copy-initialized to argc
delete p; // safe to delete a null pointer
}
在这种情况下,为什么n不能直接初始化为argc?
编辑:这里的问题已经回答了这个问题的一部分:静态变量初始化?但我不认为这是重复的,因为另一个问题中的答案没有回答我的第二个问题,即为什么2阶段初始化。此外,另一篇文章的标题并没有真正说明问题所在。
开发C的操作系统上的行为塑造了这些标准规定。当应用程序加载时,操作系统加载程序为BSS提供一些内存。最好将其清除为零,因为如果其他进程早些时候使用过该内存,则您启动的程序可能会窥探前一进程的内存内容,从而可能看到密码、对话或其他数据。并不是每个早期或简单的操作系统都关心这一点,但大多数操作系统都这样做,所以在大多数情况下,初始化实际上是"免费的",因为这是操作系统无论如何都会做的任务。
默认值为0使实现可以很容易地看到动态初始化期间设置的引用标志,因为不会有未初始化的内存读取和由此产生的未定义行为。例如,给定。。。
void f() { static int n = g(); }
编译器/实现也可以隐式地添加类似static bool __f_statics_initialised
变量的东西——幸运的是,由于归零行为,该变量默认为0
/false
——以及类似于(可能是的线程安全版本)的初始化代码。。。
if (!__f_statics_initialised)
{
n = g();
__f_statics_initialised = true;
}
对于上面的场景,初始化是在第一次调用时完成的,但对于全局变量,它是在调用main()
之前的某个时间按未指定的每个对象排序完成的。在这种情况下,有一些特定于对象的初始化代码和动态初始化能够区分未初始化状态下的静态和他们知道需要设置为非零值的静态,这使得编写健壮的启动代码变得更容易。例如,函数可以检查非本地静态指针是否仍然为0,如果是,则new
是它的对象
同样值得注意的是,许多CPU都有高效的指令来清空大量内存。
全局的零初始化是"免费的",因为它们的存储在main()启动之前分配在"BSS"段中。也就是说,当您访问指针p
时,指针本身必须存储在某个地方,而这个地方实际上是BSS中的一个特定的比特块。既然它必须初始化为某个值,为什么不为零呢?
现在,为什么自动/堆栈变量不这样做呢?因为这会花费时间:堆栈上的分配只不过是增加(或减少,从长远来看)堆栈指针。无论那里有什么垃圾,都可以留在那里(根据C的说法)。由于我们不能免费获得zero-init,所以我们根本得不到它(因为它是C,我们不喜欢为不使用的东西付费)。
默认初始化std::string或其他类类型有点复杂:C++要求以某种方式初始化它,默认构造函数当然是被使用的构造函数,是的,从技术上讲,它首先是零初始化的,但正如所讨论的,零初始化是"免费的"。"对于一个能够充分分析std::string以在构建时确定如何像调用默认构造函数一样初始化其位的实现来说,这可能是允许的,但我不知道是否有任何实现能做到这一点
在C中,全局变量和静态变量在程序的生存期内具有固定的内存地址。这使得程序启动器能够通过将适当的内存区域从可执行文件复制到计算机内存来初始化它们。
因此,C可以(必须)为每个静态/全局变量提供一个初始值。如果用户没有提供任何值,则标准行为是使用零。和局部变量相反,这既不会增加内存,也不会提高应用程序的速度(因为无论如何都必须写入值)。
最终,如果您有没有任何初始数据的大型数组,这种行为(将静态初始数据复制到可执行文件中)可能会非常糟糕。事实上,现代C编译器似乎能够避免这种浪费,并将零填充大型数组,而不是将零存储在可执行程序中。然而,一旦给出了规则,即使用户可能不需要,他们也会被迫填充该区域。无论如何,这是一个非常便宜的操作,只在程序启动时执行一次。
- 初始值设定项列表是否只接受使用相同类型的值初始化变量?
- 在 C++ 中访问 lambda 捕获初始化变量
- 为什么C++不支持对未初始化变量进行智能分析?
- 使用 clang++ 和 g++ 在C++中初始化变量
- C++使用 lambda 初始化变量
- 如何在初始化列表中的构造函数之后初始化变量/对象?
- C++ - 输出与初始化变量不同?
- C++/Win32 构造函数不使用从对话框获取的字符串初始化变量
- 在C++中,为什么int可以使用new运算符初始化变量,而double不能
- 具有静态存储持续时间的常量初始化变量的初始化顺序
- C++ 中的初始化变量
- C++中未初始化变量的值
- 如何在需要提及需要循环声明的其他类的类中初始化变量?
- 我想知道在构造函数中初始化变量时的生命周期
- 读取恰好具有良好值的未初始化变量
- 如何在 getter 的父类中初始化变量的情况下访问子类中的变量
- 为什么在 c++ 中有多种初始化变量的方法
- 没有参数的默认构造函数是否总是初始化变量?
- 是否可以使用 lambda 初始化变量(删除复制 ctor 时)
- 使用构造函数跳闸UB的新放置后使用初始化变量