【问题标题】:Initial value of int array in C - why?C中int数组的初始值 - 为什么?
【发布时间】:2019-07-03 13:58:53
【问题描述】:

为什么是值

int array[10];

在函数中声明时未定义,在声明为static时为0-initialized?

我一直在阅读this question的答案,很明显

[函数中的表达式int array[10];]的意思是:在不做任何初始化的情况下获得10-int-size内存区域的所有权。如果数组在函数中被声明为全局数组或静态数组,则所有元素如果尚未初始化,则将被初始化为零。

问题:为什么会有这种行为?编译器程序员是否决定(出于特定原因)?使用的特定编译器可以做不同的事情吗?

我问这个问题的原因:我问这个问题是因为我想让我的代码在架构/编译器之间移植。为了确保它,我知道我总是可以初始化声明的数组。但这意味着我只会为这次手术浪费宝贵的时间。那么,哪个是正确的决定?

【问题讨论】:

  • 如果数组是静态/全局的 - 那么它是零初始化的。如果它是本地/自动的 - 那么它不会被初始化。因为标准是这样说的。
  • "问题:为什么会有这种行为?"不初始化局部变量的理由是执行速度。在使用它们之前将所有变量设置为零会产生执行开销。这与 malloc 不将内存初始化为零的原因相同。
  • 我不认为这是一项要求,但大多数编译器会将静态数据放入 .bss 数据段中,当应用程序加载到内存中时,该数据段将初始化为零。
  • 为什么是为了速度。静态数据通常都聚集在一起并在一个大的 .bss 块中声明。大多数对象加载器都有一种在程序启动时将大块内存归零的有效方法。另一方面,如果您不需要,每次输入函数时将每个数组分配归零会花费一些处理时间。
  • @Leos313 不。静态数据也必须初始化,但它是由main之前发生的运行时库/启动代码完成的。但它只发生一次,而不是每次程序运行可以多次执行的函数。

标签: c arrays


【解决方案1】:

自动int array[10]; 不会隐式归零,因为归零需要时间,您可能不需要归零。此外,您不仅要支付一次成本,而且每次控制超出初始化变量时都要支付。

静态/全局 int array[10]; 隐式归零,因为静态/全局是在加载时分配的。内存将是来自操作系统的新内存,如果操作系统完全具有安全意识,那么内存将已经被清零。否则加载代码(操作系统或动态链接器)将不得不将它们归零(因为 C 标准需要它),但它应该能够在一次调用 memset 时为所有全局变量/静态变量执行此操作,这是相当可观的比一次清零每个静态/全局变量更有效。

此初始化只进行一次。即使函数内部的statics 也只初始化一次,即使它们具有非零初始化器(例如,static int x = 42;。这就是 C 要求静态的初始化器是常量表达式的原因)。

由于所有全局变量/静态变量的加载时间归零要么是操作系统保证的,要么是可有效实现的,它也可能是标准保证的,从而使程序员的生活更轻松。

【讨论】:

    【解决方案2】:

    这些值不是未定义的而是不确定的,它的行为方式是因为标准如此规定。

    C standard 的第 6.7.9p10 节关于初始化状态:

    如果具有自动存储持续时间的对象不是 显式初始化,其值是不确定的。如果 具有静态或线程存储持续时间的对象不是 显式初始化,然后:

    • 如果有指针类型,则初始化为空指针;
    • 如果它具有算术类型,则将其初始化为(正或无符号)零;
    • 如果是聚合,则每个成员都根据这些规则(递归)初始化,并且任何填充都初始化为零位;
    • 如果是联合,则根据这些规则(递归)初始化第一个命名成员,并初始化任何填充 零位;

    因此,对于在文件范围或static 定义的任何变量,您可以放心地假设这些值是零初始化的。对于在函数或作用域内声明的变量,您不能对未初始化的变量做出任何假设。

    至于为什么,全局/静态变量是在程序启动甚至编译时初始化的,而局部变量每次进入作用域时都必须初始化,这样做需要时间。

    【讨论】:

    • 谢谢。所以我可以放心地决定在数组是静态的时候忽略零初始化:这种行为永远不会改变
    • @Leos313 正确。
    • 正确,尽管通常编译器/链接器会在静态数组上优化 ={0},因此使用该形式没有害处。
    • 在独立环境中要小心。如果没有运行时库 (AKA crt.o),有责任提供启动代码,确保在输入 main 之前将 .bss 清零。
    【解决方案3】:

    在堆栈分配/局部变量中没有定义变量的初始值的原因是效率。 C 标准希望您的程序分配您的数组并稍后填充它:

    int array[10];
    for (i = 0; i < 10; ++i)
        array[i] = i * 42;
    

    在这种情况下,任何初始化都是没有意义的,所以 C 标准想要避免它。

    如果您的程序需要将这些值初始化为零,您可以明确地这样做:

    int array[10] = {0}; // initialize to zero so the accumulation below works
    while (condition)
    {
        ... // some code
        for (i = 0; i < 10; ++i)
            array[i] += other_array[i];
    }
    

    是否初始化由您决定,因为您应该知道程序的行为方式。对于不同的数组,这个决定会有所不同。

    但是,此决定不取决于编译器 - 它们都符合标准。关于可移植性的一个小细节——如果你没有初始化你的数组并且当你使用特定的编译器时仍然看到其中的所有零——不要被愚弄;这些值仍然未定义;你不能指望他们是 0。

    其他一些语言认为零初始化足够便宜,即使它是多余的,而且它的优势(安全性)超过了它的劣势(性能)。在 C 中,性能更重要,所以它决定不这样做。

    【讨论】:

      【解决方案4】:

      C 的哲学是 a) 始终信任程序员 b) 优先考虑执行速度而不是程序员的便利性。 C 假设程序员最有能力知道数组(或任何其他auto 变量)是否需要 初始化为特定值,如果需要,则足够聪明地编写代码自己做。否则不会浪费 CPU 周期。

      对数组访问进行边界检查也是如此,NULL 检查指针取消引用也是如此。

      这同时是 C 语言的最大优势(快速代码,占用空间小)和最大的弱点(大量体力劳动以使代码安全可靠)。

      【讨论】:

        猜你喜欢
        • 2010-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-05
        • 2017-05-30
        • 2023-01-19
        相关资源
        最近更新 更多