代码之家  ›  专栏  ›  技术社区  ›  Konrad

C++中数组的初始化

  •  5
  • Konrad  · 技术社区  · 15 年前

    我看到的每一个地方都有人大声争辩说,未初始化的变量是不好的,我当然同意并理解为什么——然而,我的问题是,有没有什么时候你不想这样做?

    例如,以代码为例:

    char arrBuffer[1024] = { '\0' };
    

    在不初始化数组的情况下,取消整个数组是否会对使用数组产生性能影响?

    10 回复  |  直到 15 年前
        1
  •  10
  •   Arkaitz Jimenez    15 年前

    我假设一个堆栈初始化,因为静态数组是自动初始化的。
    G+输出

       char whatever[2567] = {'\0'};
       8048530:       8d 95 f5 f5 ff ff       lea    -0xa0b(%ebp),%edx
       8048536:       b8 07 0a 00 00          mov    $0xa07,%eax
       804853b:       89 44 24 08             mov    %eax,0x8(%esp)
       804853f:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)
       8048546:       00 
       8048547:       89 14 24                mov    %edx,(%esp)
       804854a:       e8 b9 fe ff ff          call   8048408 <memset@plt>
    

    所以,用'\0'初始化,调用memset就完成了,所以是的,性能会受到影响。

        2
  •  7
  •   spoulson    15 年前

    如果变量是全局变量或静态变量,则其数据通常按原样存储在已编译的可执行文件中。所以,你的 char arrBuffer[1024] 将可执行文件大小增加1024字节。初始化它将确保可执行文件包含您的数据,而不是默认的0或编译器选择的任何内容。当程序启动时,不需要处理来初始化变量。

    另一方面,堆栈上的变量(如非静态局部函数变量)不以相同的方式存储在可执行文件中。相反,在函数输入时,空间分配在堆栈上,memcpy将数据放入变量中,从而影响性能。

        3
  •  4
  •   paxdiablo    15 年前

    规则是变量应该在使用前设置。

    你做 如果您知道在使用之前将在其他地方设置它们,则必须在创建时显式初始化它们。

    例如,以下代码完全正常:

    int main (void) {
        int a[1000];
        : :
        for (int i =0; i < sizeof(a)/sizeof(*a); i++)
            a[i] = i;
        : :
        // Now use a[whatever] here.
        : :
        return 0;
    }
    

    在这种情况下,在数组创建时初始化它是浪费的。

    至于是否存在性能惩罚,部分取决于变量的定义位置,部分取决于执行环境。

    C标准保证使用静态存储持续时间定义的变量(在文件级或作为函数中的静态变量)首先初始化为所有零的位模式,然后设置为各自的初始化值。

    它确实 授权如何完成第二步。一种典型的方法是让编译器本身创建初始化变量并将其放入可执行文件中,这样就可以通过加载可执行文件来初始化它。这不会对性能产生影响(对于初始化,显然它会对程序加载产生一些影响)。

    当然,实现可能希望节省可执行文件中的空间,并使用代码初始化这些变量(在调用main之前)。这个 对性能有影响,但可能很小。

    对于那些具有自动存储持续时间的变量(局部变量等),除非为它们分配了一些内容,否则它们永远不会被隐式初始化,因此也会有性能损失。“从不隐式初始化”是指代码段:

    void x(void) {
        int x[1000];
        ...
    }
    

    将导致x[]具有不确定的值。但从那时起:

    void x(void) {
        int x[1000] = {0};
    }
    

    可能只会导致1000 integer memcpy类型的操作(在这种情况下更可能是memset),这也可能很快。您只需要记住初始化将发生 每一个 调用该函数的时间。

        4
  •  2
  •   pmg    15 年前

    量度!

    #include <stdio.h>
    #include <time.h>
    
    int main(void) {
      clock_t t0;
      int k;
    
      t0 = clock();
      for (k=0; k<1000000; k++) {
        int a[1000];
        a[420] = 420;
      }
      printf("Without init: %f secs\n", (double)(clock() - t0) / CLOCKS_PER_SEC);
    
      t0 = clock();
      for (k=0; k<1000000; k++) {
        int a[1000] = {0};
        a[420] = 420;
      }
      printf("   With init: %f secs\n", (double)(clock() - t0) / CLOCKS_PER_SEC);
    
      return 0;
    }
    
    $ gcc measure.c
    $ ./a.out
    Without init: 0.000000 secs
       With init: 0.280000 secs
    $ gcc -O2 measure.c
    $ ./a.out
    Without init: 0.000000 secs
       With init: 0.000000 secs
    
        5
  •  0
  •   user212328    15 年前

    对于大型阵列,性能影响可能非常显著。默认情况下初始化所有变量实际上并没有提供很多好处。它不是坏代码的解决方案,而且它可能隐藏实际问题,否则编译器可能会发现这些问题。您需要跟踪所有变量在其整个生命周期中的状态,以使您的代码无论如何都是可靠的。

        6
  •  0
  •   Kaz Dragon    15 年前

    回答你的问题:它 可以 对性能有影响。编译器可能会检测到数组的值未被使用,而不使用它们。这是可能的。

    我个人认为这是个人风格的问题。我想说的是:让它不被初始化,然后使用一个类似lint的工具来告诉你是否在使用它,它肯定是一个bug(而不是使用默认值,而不是被告知,这也是一个bug,但是是一个沉默的bug)。

        7
  •  0
  •   cuteCAT    15 年前

    我认为要求在声明时默认初始化所有变量是一个坏建议。在大多数情况下,这是不必要的,并带有绩效惩罚。

    例如,我经常使用下面的代码将数字转换为字符串:

    char s[24];
    sprintf(s, "%d", int_val);
    

    我不会写:

    char s[24] = "\0";
    sprintf(s, "%d", int_val);
    

    现代编译器能够在不初始化的情况下判断是否使用变量。

        8
  •  0
  •   Stack Overflow is garbage    15 年前

    变量应初始化为 有意义 价值。盲目而幼稚地将所有内容设置为零并不比不进行初始化好多少。它可能会导致无效的代码崩溃,而不是不可预知的行为,但它不会导致代码崩溃。 对的 .

    如果在创建数组时只是为了避免未初始化的变量而天真地将其归零,那么它仍然是 逻辑上 未初始化。它还没有在应用程序中有意义的值。

    如果要初始化变量(并且应该这样做),请为它们提供在应用程序中有意义的值。其余的代码是否希望数组最初为零?如果是,将其设置为零。否则将其设置为其他有意义的值。

    或者,如果您的代码的其余部分希望在不首先读取数组的情况下写入该数组,那么无论如何都不要对其进行初始化。

        9
  •  0
  •   Stephen Nutt    15 年前

    我个人反对在created初始化数组。考虑下面两段代码。

    char buffer[1024] = {0};
    for (int i = 0; i < 1000000; ++i)
    {
      // Use buffer
    }
    

    VS

    for (int i = 0; i < 1000000; ++i)
    {
      char buffer[1024] = {0};
      // Use buffer
    }
    

    在第一个例子中,为什么要费心初始化缓冲区,因为循环缓冲区周围的第二次不再初始化0?除了第一次迭代外,我对缓冲区的使用必须在没有初始化的情况下工作。如果我通常只执行一次循环,那么初始化所做的就是消耗时间、膨胀代码和隐藏错误。

    虽然我可以将代码重新考虑为第二个示例,但是如果我可以重新编写代码,这样就不需要初始化循环中的缓冲区,我真的想将其归零吗?

    我怀疑现在大多数编译器都有用非0值填充未初始化变量的选项。我们以这种方式运行所有的调试构建,以帮助检测未初始化变量的使用,并且在发布模式下,我们关闭选项,以使变量真正未初始化。正如胡舍伍德所说,一些编译器可以注入代码来帮助检测未初始化变量的使用。

    编辑: 在上面的代码中,我正在将缓冲区初始化为值0(而不是字符“0”),这相当于用'\0'初始化缓冲区。

    为了进一步澄清我的第一个代码片段,请想象下面的人为示例。

    char buffer[1024] = {0};
    for (int i = 0; i < 1000000; ++i)
    {
      // Buffer is 0 initialized, so it is fine to call strlen
      int len = strlen (buffer);
      memset (buffer, 'a', 1024);
    }
    

    第一次通过循环,缓冲区初始化为0,因此strlen将返回0。第二次通过循环时,缓冲区不再初始化为0,实际上不包含单个0字符,因此strlen的行为未定义。

    既然您同意我的观点,如果初始化了缓冲区,那么在循环内部移动缓冲区是不可取的,而且我已经表明在循环外部初始化缓冲区不提供任何保护,为什么还要初始化它呢?

        10
  •  -2
  •   Priyank Bolia    15 年前

    为什么您关心性能的好处,不初始化它将获得多少性能,并且它是否比由于垃圾指针而在调试期间节省的时间更多。