为什么编译器将零放入数组而不必? [英] Why compilers put zeros into arrays while they do not have to?

查看:211
本文介绍了为什么编译器将零放入数组而不必?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解编译器应该何时初始化数组和何时应该默认初始化它。我试图两个选项:一个原始数组,另一个数组聚合在一个结构:

  const int N = 1000; 

struct A
{
uint32_t arr [N];

A()= default;
};

void print(uint32_t * arr,const std :: string& message)
{
std :: cout<消息< :<<
(std :: count(arr,arr + N,0)== N?all zeros:garbage)< std :: endl;
}

int main()
{
uint32_t arrDefault [N];
print(arrDefault,Automatic array,default initialization);

uint32_t arrValue [N] = {};
print(arrValue,Automatic array,value initialization);

uint32_t * parrDefault = new uint32_t [N];
print(parrDefault,动态数组,默认初始化);

uint32_t * parrValue = new uint32_t [N]();
print(parrValue,动态数组,值初始化);

A structDefault;
print(structDefault.arr,自动结构,默认初始化);

structValue {};
print(structValue.arr,自动结构,值初始化);

A * pstructDefault = new A;
print(pstructDefault-> arr,动态结构,默认初始化);

A * psstructValue = new A();
print(psstructValue-> arr,动态结构,值初始化);
}

这是我看到的 clang VC ++



自动数组,默认初始化:garbage
自动数组,值初始化:全零
动态数组,默认初始化:garbage
动态数组,值初始化:全零
自动结构,默认初始化:全零
自动结构,值初始化:全零
动态结构,默认初始化:garbage
动态struct,value initialization:all zeros

输出 gcc 仅在第一行不同,其中还放置全零。



从我的角度来看,他们都错了,我预期的是:

 自动数组,默认初始化:garbage 
自动数组,值初始化:全零
动态数组,默认初始化:garbage
动态数组,值初始化:全零
自动结构,默认初始化:garbage
自动结构,值初始化:garbage
动态结构,默认初始化:garbage
动态结构,值初始化:garbage

输出对于原始数组是正确的(除了gcc):我们有默认的垃圾和值的零。大。但对于一个结构,我期望有垃圾所有的时间。从默认初始化



< blockquote>

默认初始化在三种情况下执行:


  1. ...


  2. 当基本类或非静态数据成员未在构造函数初始值列表中提及并调用该构造函数时。

默认初始化的效果是:




  • POD(直到C ++ 11)类类型,...

  • 如果T是数组类型,数组的每个元素都是
    default-initialized;

  • 否则不执行任何操作:自动存储持续时间的对象(及其子对象)初始化为不确定的值。


在我的示例中,我有一个非静态数据成员,没有在构造函数初始化列表中提到,它是一个POD类型的数组。



我的问题是:

ul>
  • 为什么编译器违反了这一点?我的意思是,为什么他们放零,当他们不必,浪费我的运行时?我的读数错了吗?

  • 如何强制执行此类行为,以确保我不会用零填充运行时填充数组?

  • 为什么gcc执行自动数组?


  • 解决方案

    structValue {}; 是聚合初始化,因此保证了0。



    由于 A没有用户提供的构造函数,因为显式默认构造函数不计算在内,这同样适用于值初始化,如 A * psstructValue = new A();



    对于默认初始化情况:读取未初始化的变量是UB,未定义的行为是未定义的。编译器可以做任何它想要的。显示您的0与崩溃的法律一样。也许在你偶然读到的记忆中甚至有0。也许编译器感觉像0初始化。从标准的角度来看,这两个都是同样好的。

    也就是说,在使用发布/优化版本测试时,你有更好的机会看到垃圾。调试版本往往会做额外的工作,帮助诊断问题,包括进行一些额外的初始化。



    (对于记录:gcc和clang与-O3似乎没有不必要的初始化在我的Linux系统上乍一看,但是,我有所有零的每一个情况。这似乎是偶然。)


    I'm trying to understand when compilers should value initialize arrays and when they should default initialize it. I'm trying two options: one raw array, another array aggregated in a struct:

    const int N = 1000;
    
    struct A 
    {
      uint32_t arr[N];
    
      A() = default;
    };
    
    void print(uint32_t* arr, const std::string& message)
    {
      std::cout << message << ": " << 
        (std::count(arr, arr + N, 0) == N ? "all zeros" : "garbage") << std::endl;
    }
    
    int main()
    {
      uint32_t arrDefault[N];
      print(arrDefault, "Automatic array,  default initialization");
    
      uint32_t arrValue[N] = {};
      print(arrValue, "Automatic array,  value   initialization");
    
      uint32_t* parrDefault = new uint32_t[N];
      print(parrDefault, "  Dynamic array,  default initialization");
    
      uint32_t* parrValue = new uint32_t[N]();
      print(parrValue, "  Dynamic array,  value   initialization");
    
      A structDefault;
      print(structDefault.arr, "Automatic struct, default initialization");
    
      A structValue{};
      print(structValue.arr, "Automatic struct, value   initialization");
    
      A* pstructDefault = new A;
      print(pstructDefault->arr, "  Dynamic struct, default initialization");
    
      A* psstructValue = new A();
      print(psstructValue->arr, "  Dynamic struct, value   initialization");
    }
    

    Here is what I see for clang and VC++:

    Automatic array,  default initialization: garbage
    Automatic array,  value   initialization: all zeros
      Dynamic array,  default initialization: garbage
      Dynamic array,  value   initialization: all zeros
    Automatic struct, default initialization: all zeros
    Automatic struct, value   initialization: all zeros
      Dynamic struct, default initialization: garbage
      Dynamic struct, value   initialization: all zeros
    

    Output for gcc is different only in the first line, where it also puts "all zeros".

    From my point of view they are all wrong, and what I expect is:

    Automatic array,  default initialization: garbage
    Automatic array,  value   initialization: all zeros
      Dynamic array,  default initialization: garbage
      Dynamic array,  value   initialization: all zeros
    Automatic struct, default initialization: garbage
    Automatic struct, value   initialization: garbage
      Dynamic struct, default initialization: garbage
      Dynamic struct, value   initialization: garbage
    

    I.e. output is ok for raw arrays (except for gcc): we have garbage for default and zeros for value. Great. But for a struct I would expect to have garbage all the time. From default initialization:

    Default initialization is performed in three situations:

    1. ...
    2. ...
    3. when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.

    The effects of default initialization are:

    • if T is a non-POD (until C++11) class type, ...
    • if T is an array type, every element of the array is default-initialized;
    • otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.

    In my example I have non-static data member that is not mentioned in a constructor initializer list, which is an array of POD type. I expect it to be left with indeterminate values, no matter how my struct is constructed.

    My questions are:

    • Why does compilers violate that? I mean, why they put zeros when they do not have to, wasting my runtime? Am I wrong in my readings?
    • How can I enforce such behavior to make sure I do not waste my runtime populating arrays with zeros?
    • Why gcc performs value initialization for an automatic array?

    解决方案

    A structValue{}; is aggregate initialization, so 0 are guaranteed.

    As A has no user provided constructor because explicitly defaulted constructors do not count as such, the same applies for value initialization as in A* psstructValue = new A();.

    For the default initialization cases: Reading uninitialized variables is UB, and Undefined behavior is undefined. The compiler can do with that whatever it wants. Showing you 0 is just as legal as crashing. Maybe there even were 0 in the memory you read by chance. Maybe the compilers felt like 0 initializing. Both equally fine from the standard's point of view.

    That being said, you have a better chance of seeing garbage when testing with Release / optimized builds. Debug builds tend to do extra stuff to help diagnosing problems, including doing some extra initialization.

    (For the record: gcc and clang with -O3 appear to do no unnecessary initialization on my Linux system at first glance. Nevertheless, I got "all zeroes" for every case. That appears to be by chance.)

    这篇关于为什么编译器将零放入数组而不必?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆