为什么文件范围静态变量必须为零初始化?

swa*_*ang 8 c c++ initialization

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
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,为什么n不能直接初始化为argc?

编辑:这个问题的一部分已经回答了这里的问题:静态变量初始化? 但我不认为这是重复的,因为另一个问题的答案没有回答我的第二个问题,即.为什么2阶段初始化.此外,另一篇文章的标题并没有真正说明究竟是什么问题.

Ton*_*roy 13

在开发C的操作系统上的行为已经形成了这些标准规定.当应用程序加载时,OS加载程序为BSS提供一些内存.将它清除为零是可取的,因为如果其他一些进程早先使用了该内存,那么您启动的程序可以窥探先前进程的内存内容,可能会看到密码,对话或其他数据.并非每个早期或简单的操作系统都关心这一点,但大多数操作系统都关心这一点,因此大多数情况下,初始化实际上是"免费的",因为操作系统无论如何都会执行这项任务.

如果默认值为0,则实现很容易看到引用动态初始化期间设置的标志,因为没有未初始化的内存读取和随后的未定义行为.例如,给定......

void f() { static int n = g(); }
Run Code Online (Sandbox Code Playgroud)

...编译器/实现可能会隐式添加类似static bool __f_statics_initialised变量的东西- "幸运"默认为0/ false由于归零行为 - 以及类似于(可能的线程安全版本)的初始化代码...

if (!__f_statics_initialised)
{
    n = g();
    __f_statics_initialised = true;
}
Run Code Online (Sandbox Code Playgroud)

对于上面的场景,初始化是在第一次调用时完成的,但是对于全局变量,它是在未指定的每对象排序中完成的,之前的某个时间main()被调用.在这种情况下,具有一些特定于对象的初始化代码和动态初始化能够区分未初始化状态下的静态与他们知道的静态需要设置为非零值,这使得编写健壮的启动代码变得更容易.例如,函数可以检查非本地静态指针是否仍为0,如果是,则检查new对象是否为0 .

值得注意的是,许多CPU都具有高效的指令,可以将大量内存归零.


Joh*_*nck 7

全局变量的零初始化是"免费"的,因为它们的存储在main()启动之前在"BSS"段中分配.也就是说,当您访问指针时p,指针本身必须存储在某处,并且某处实际上是BSS中的特定位块.既然必须将其初始化为某些东西,为什么不归零?

现在,为什么不自动/堆栈变量这样做?因为这会花费时间:堆栈上的分配只不过是堆栈指针的递增(或递减,透视).无论什么垃圾都可以留在那里(根据C).由于我们无法免费获得零init,因此我们根本没有得到它(因为它是C,我们不喜欢支付我们不使用的东西).

默认初始化std :: string或其他类类型有点复杂:C++要求它以某种方式初始化,默认构造函数当然是使用它,是的,从技术上讲它首先是零初始化,但正如所讨论的那样,零初始化是"免费的".可能允许一个能够充分分析std :: string的实现,以便在构建时确定如何初始化其位,就像调用默认构造函数一样,但我不知道是否有任何实现.

  • `.bss`实际上并没有占用磁盘上的存储空间(看点是什么,因为它全部为零?); 它只描述了一个可以分配偏移的存储器范围. (2认同)