Почему при инициализации структуры 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
... это означает, что только ПЕРВЫЙ член был установлен в ноль, а все остальные были установлены в значения по умолчанию.
У меня всегда было впечатление, что инициализация структуры любым из этих трех способов приведет к ее нулевой инициализации, но, очевидно, я ошибаюсь!
data_t d{}
data_t d = {}
data_t d = {0}
Поэтому мой ключевой вывод из этого ответа таков:
Большой вывод здесь заключается в том, что НИЧЕГО из этого:
data_t d{}
,data_t d = {}
, а такжеdata_t d = {0}
, фактически установить для всех членов структуры ноль!
data_t d{}
устанавливает для всех значений значения по умолчанию, определенные в структуре.data_t d = {}
также устанавливает для всех значений значения по умолчанию.- А также
data_t d = {0}
устанавливает только ПЕРВОЕ значение на ноль, а все остальные значения на их значения по умолчанию.
Итак, почему не инициализировать структуру C++ для = {0}
установить для всех его членов значение 0?
Обратите внимание, что мои ключевые выводы, приведенные выше, фактически противоречат этой довольно официальной документации, которую я использовал в течение многих лет ( https://en.cppreference.com/w/cpp/language/zero_initialization), в которой говорится, чтоT t = {} ;
а также T {} ;
оба являются нулевыми инициализаторами, тогда как на самом деле, согласно моим тестам и выводам выше, они НЕ.
Ссылки:
- Как инициализировать структуру до 0 в C++
- Обновление: меня тоже только что указали на эту ссылку: что означает {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
, выполняет совокупную инициализацию: предоставленный0
value используется для инициализации первого члена, а остальные члены инициализируются с использованием соответствующих значений по умолчанию, и, если их не существует, инициализируются значением (выделено мной, отредактировано для 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
.