Почему при инициализации структуры C++ значением `= {0}` для всех ее членов не устанавливается значение 0?

После тонны тестирования и написания этого ответа (примечание: отрицательное голосование было ДО того, как я его полностью переписал), я не могу понять, почему= {0} не устанавливает все члены структуры в ноль!

Если вы сделаете это:

struct data_t
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
};

data_t d3 = {0};
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
       d3.num1, d3.num2, d3.num3, d3.num4);

... вывод:

d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150

... хотя я ожидал, что результат будет таким:

d3.num1 = 0
d3.num2 = 0
d3.num3 = 0
d3.num4 = 0

... это означает, что только ПЕРВЫЙ член был установлен в ноль, а все остальные были установлены в значения по умолчанию.

У меня всегда было впечатление, что инициализация структуры любым из этих трех способов приведет к ее нулевой инициализации, но, очевидно, я ошибаюсь!

  1. data_t d{}
  2. data_t d = {}
  3. data_t d = {0}

Поэтому мой ключевой вывод из этого ответа таков:

Большой вывод здесь заключается в том, что НИЧЕГО из этого: data_t d{}, data_t d = {}, а также data_t d = {0}, фактически установить для всех членов структуры ноль!

  1. data_t d{} устанавливает для всех значений значения по умолчанию, определенные в структуре.
  2. data_t d = {} также устанавливает для всех значений значения по умолчанию.
  3. А также data_t d = {0} устанавливает только ПЕРВОЕ значение на ноль, а все остальные значения на их значения по умолчанию.

Итак, почему не инициализировать структуру C++ для = {0} установить для всех его членов значение 0?

Обратите внимание, что мои ключевые выводы, приведенные выше, фактически противоречат этой довольно официальной документации, которую я использовал в течение многих лет ( https://en.cppreference.com/w/cpp/language/zero_initialization), в которой говорится, чтоT t = {} ; а также T {} ; оба являются нулевыми инициализаторами, тогда как на самом деле, согласно моим тестам и выводам выше, они НЕ.

Ссылки:

  1. Как инициализировать структуру до 0 в C++
  2. Обновление: меня тоже только что указали на эту ссылку: что означает {0} при инициализации объекта?

3 ответа

Решение

Итак, почему при инициализации структуры C++ = {0} для всех ее членов не устанавливается значение 0?

Потому что вы предоставляете только одно значение, а класс имеет более одного члена.

Когда у тебя есть T t{}; или T t = {}то, что вы делаете, называется инициализацией значения. При инициализации значения, если объект / член не имеет конструктора по умолчанию или инициализатора члена по умолчанию, компилятор возвращается к нулю, инициализируя объект / член. Так что с

data_t d{}

значение элементов по порядку будет 100, -100, 0,150 и это 0 за num3 происходит потому, что для него нет значения по умолчанию, и вы не указали значение в {} поэтому компилятор возвращается к нулю, инициализируя num3. То же самое и сdata_t d = {}. Сdata_t d = {0} вы предоставляете первый элемент, поэтому num1 является 0, но затем, как и первые два, все остальные члены инициализируются своим значением по умолчанию, если они есть, или нулем, если они этого не делают, что дает вам 0, -100, 0, 150 для значений элементов.

Это было изменение, которое произошло, когда был выпущен C++11 и позволил использовать инициализаторы членов по умолчанию.


Если твой data_t был определен как

typedef struct
{
    int num1;
    int num2;
    int num3;
    int num4;
} data_t;

тогда data_t d{}, data_t d = {}, data_t d = {0}все оставит вас с нулевым инициализированным классом, поскольку нет инициализаторов членов по умолчанию и единственное значение, которое вы предоставляете в своем списке инициализации в квадратных скобках (техническое имя для{...}) равен нулю, поэтому все члены становятся нулевыми.

data_t d3 = {0}- это синтаксис инициализации списка, который с такими агрегатами, какdata_t, выполняет совокупную инициализацию: предоставленный0value используется для инициализации первого члена, а остальные члены инициализируются с использованием соответствующих значений по умолчанию, и, если их не существует, инициализируются значением (выделено мной, отредактировано для C++14):

Если количество предложений инициализатора меньше количества членов или список инициализаторов полностью пуст, оставшиеся члены инициализируются их инициализаторами членов по умолчанию, если они указаны в определении класса, а в противном случае - пустыми списками в соответствии с обычным списком. - правила инициализации (которые выполняют инициализацию значений для неклассовых типов и неагрегатных классов с конструкторами по умолчанию и агрегатную инициализацию для агрегатов). Если член ссылочного типа является одним из этих оставшихся членов, программа плохо сформирована.

значение-инициализация означает нулевую инициализацию для неклассовых типов. Вот почему членnum3, который не имеет значения по умолчанию, получает значение 0.

Примечание: это не следует путать с инициализацией по умолчанию, которая вообще не инициализирует неклассовые типы.data_t d3;будет инициализация по умолчанию, а членnum3 останется в неопределенном состоянии.

Важным моментом, на который следует обратить внимание, является то, является ли инициализируемый объект агрегатом, потому что правила инициализации отличаются для агрегатов и классов с конструктором. В случае конструктора члены класса, не являющиеся членами класса, без значения по умолчанию будут инициализированы по умолчанию (т.е. оставлены в неопределенном состоянии).

Некоторые примеры:

struct A { // an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
};

struct B { // not an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
    B() {}
    B(int) {}
};

int main() {
    A a1; // default-initialization: a1 is {100, -100, ???}
    A a2 = {}; // aggregate initialization: a2 is {100, -100, 0}
    A a3 = { 1 }; // aggregate initialization: a3 is {1, -100, 0}
    A a4 = { 1,2,3 }; // aggregate initialization: a4 is {1, 2, 3}
    B b1; // default-initialization: b1 is {100, -100, ???}
    B b2 = {}; // copy-list-initialization invoking B::B(): b2 is {100, -100, ???}
    B b3 = { 1 }; // copy-list-initialization invoking B::B(int): b3 is {100, -100, ???}
    B b4 = { 1,2,3 }; // error: no B constructor taking (int,int,int)
}

Также обратите внимание, что правила агрегированной инициализации предшествуют C++11. См., Например, связанный с этим вопрос до C++11: что означает {0} при инициализации объекта?

Краткий ответ: потому что у вас есть инициализаторы класса.

Более длинный ответ: поскольку вы компилируете для C++14 или выше, у вас есть инициализаторы класса и вы используете агрегатную инициализацию. Ссылка дает объяснение:

Если количество предложений инициализатора меньше количества членов или список инициализаторов полностью пуст, оставшиеся члены инициализируются их инициализаторами членов по умолчанию, если они указаны в определении класса, и в противном случае (начиная с C++14) пустыми списками, в соответствии с обычными правилами инициализации списков (которые выполняют инициализацию значений для неклассовых типов и неагрегатных классов с конструкторами по умолчанию, а также агрегатную инициализацию для агрегатов).

Чтобы обнулить все члены данных, предоставьте только объявления членов данных без инициализаторов класса, а затем используйте= {0} или ={} синтаксис:

struct data_t
{
    int num1;
    int num2;
    int num3;
    int num4;
};

int main()
{
    data_t d3 = { 0 };
    printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
        d3.num1, d3.num2, d3.num3, d3.num4);
}

Теперь все ваши элементы данных инициализированы для 0.

Другие вопросы по тегам