Что означает {0} при инициализации объекта?

Что это означает, когда {0} используется для инициализации объекта? Я нигде не могу найти никаких ссылок на {0}, и из-за фигурных скобок поиск в Google бесполезен.

Пример кода:

SHELLEXECUTEINFO sexi = {0}; // what does this do?
sexi.cbSize = sizeof(SHELLEXECUTEINFO);
sexi.hwnd = NULL;
sexi.fMask = SEE_MASK_NOCLOSEPROCESS;
sexi.lpFile = lpFile.c_str();
sexi.lpParameters = args;
sexi.nShow = nShow;

if (ShellExecuteEx(&sexi))
{
    DWORD wait = WaitForSingleObject(sexi.hProcess, INFINITE);
    if (wait == WAIT_OBJECT_0)
        GetExitCodeProcess(sexi.hProcess, &returnCode);
}

Без него приведенный выше код выйдет из строя во время выполнения.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
255
0
60 354
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Ответ принят как подходящий

То, что здесь происходит, называется инициализацией совокупность. Вот (сокращенное) определение агрегата из раздела 8.5.1 спецификации ISO:

An aggregate is an array or a class with no user-declared constructors, no private or protected non-static data members, no base classes, and no virtual functions.

Теперь использование {0} для инициализации подобного агрегата - это, по сути, трюк с 0 в целом. Это связано с тем, что при использовании агрегированной инициализации вам не нужно указывать всех участников и спецификация требует, чтобы все неуказанные элементы были инициализированы по умолчанию, что означает установку 0 для простых типов.

Вот соответствующая цитата из спецификации:

If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be default-initialized. Example:

struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };

initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is, 0.

Вы можете найти полную спецификацию по этой теме здесь

Отличный ответ. Просто хотел добавить, что инициализация агрегата с помощью {0} аналогична его инициализации с помощью простого {}. Возможно, первое делает более очевидным обнуление встроенных типов.

James Hopkin 18.09.2008 13:02

Некоторые компиляторы подавляются {}, поэтому {0} используется

Branan 18.09.2008 20:11

Не совсем .. В C++, если первый член не может быть построен с нуля, {0} не будет работать. Пример: структура A {B b; int i; char c; }; структура B {B (); B (строка); }; A a = {}; // этот оператор нельзя переписать как 'A a = {0}'.

Aaron 23.09.2008 19:45

@Branan, это потому, что в C "{}" недопустимо. В C++ это так. @ don.neufeld, это изменилось с C++ 03 (инициализация по умолчанию -> инициализация значения). Имейте в виду, что цитата цитирует стандарт C++ 98.

Johannes Schaub - litb 10.07.2009 16:36

Итак, заменяет ли это необходимость использования ZeroMemory () (в C++)?

Ray 25.03.2014 23:44

@DebugErr Хотя есть много случаев, когда любой из них будет работать, остаются случаи, когда Microsoft ZeroMemory или стандартный memset не могут работать (в основном, для типов, не относящихся к POD), а также случаи, когда агрегированная инициализация не может работать (в основном, для массивов, где размер не известен статически).

user743382 31.07.2014 01:11

Стандартные цитаты @DonNeufeld должны сопровождаться названием цитируемого стандарта; в данном случае это очень актуально, потому что вы процитировали C++ 98, но C++ 03 изменил инициализированный по умолчанию на инициализированное значение. C++ 03 также изменил значение терминов; инициализированный по умолчанию для примитивных типов в C++ 98 означает все биты-ноль, но в C++ 03 означает неинициализированный. Более поздние стандарты (после того, как вы написали свой ответ) следуют модели C++ 03.

M.M 24.11.2015 05:47

@Aaron не уверен, что указание конструктора для B по-прежнему будет работать. поскольку он может лишить граф объектов права называться агрегатным типом. (или я ошибаюсь?)

v.oddou 23.05.2017 05:29

Прошло некоторое время с тех пор, как я работал с c / C++, но IIRC, тот же ярлык можно использовать и для массивов.

Я также использую его для инициализации строк, например.

char mytext[100] = {0};

Хотя, конечно, если mytext используется как строка, char mytext [100]; mytext [0] = '\ 0'; будет иметь тот же эффект, что и пустая строка, но приведет к обнулению реализации только первого байта.

Chris Young 08.04.2009 20:32

@Chris: Мне часто хотелось иметь синтаксис для частичной инициализации объекта. Возможность «объявить и инициализировать» x, а затем сделать то же самое с y, а затем z, намного лучше, чем необходимость объявлять x, y и z, а затем инициализировать x, y и z, но инициализировать 100 байтов только тогда, когда действительно нужна инициализация, кажется довольно расточительной.

supercat 13.12.2017 02:00

Следует помнить, что этот метод не устанавливает байты заполнения в ноль. Например:

struct foo
{
    char c;
    int  i;
};

foo a = {0};

Не то же самое, что:

foo a;
memset(&a,0,sizeof(a));

В первом случае байты заполнения между c и i не инициализируются. Зачем тебе это нужно? Что ж, если вы сохраняете эти данные на диск или отправляете их по сети или что-то еще, у вас может быть проблема с безопасностью.

Конечно, проблема безопасности возникает только в том случае, если вы «пишете (f, & a, sizeof (a))», что может привести к разной кодировке файлов на разных процессорах / компиляторах. Хорошо отформатированный вывод был бы безопасен без memset.

Aaron 23.09.2008 19:42

Кроме того, если вы отправляете материал по сети, вы всегда устанавливаете выравнивание для упаковки. Таким образом вы получите как можно меньше дополнительных байтов заполнения.

Mark Kegel 09.11.2008 01:36

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

Tomas 30.07.2010 13:15

Я хотел бы оспорить использование слов «не является». Байты заполнения определяются реализацией. Компилятор может по своему усмотрению превратить foo a = {0} в memset (& a, 0, sizeof (a)). Не требуется «пропускать» байты заполнения, и Только устанавливает foo.c и foo.i. +1 за (потенциальную) ошибку безопасности

SecurityMatt 17.02.2013 00:59

@HaroldEkstrom обновление этого ответа: как в C11, так и в C++ 11, биты заполнения явно должны быть установлены в ноль этим методом.

Leushenko 06.05.2015 12:27

@Leushenko у вас есть ссылка на это? Насколько мне известно, это верно только для объектов со статической продолжительностью хранения.

M.M 24.11.2015 05:53

@ M.M на самом деле вы правы, явная ссылка на заполнение находится в разделе о статических объектах; однако «остальная часть агрегата должна быть неявно инициализирована так же, как объекты, которые имеют статическую продолжительность хранения» (C11 6.7.9). Я предполагаю, что технически вы могли бы повернуть это так, чтобы это означало, что биты заполнения пропускаются. если только вы предоставляете явные инициализаторы для всех членов, но это потребует от поставщика компилятора изо всех сил, чтобы троллить вас. Неоднозначный текст, но я думаю, что намерение состоит в том, чтобы обнулить отступы.

Leushenko 24.11.2015 06:26

В пункте 19 @Leushenko говорится, что «все подобъекты, которые не инициализированы явно», поэтому я бы склонился к пункту 21 (который вы цитировали), будучи небрежным. Было бы поистине странно, если бы спецификация позволяла неинициализировать заполнение до момента последнего инициализатора, после которого заполнение должно было быть обнулено, особенно учитывая, что инициализаторы могут появляться не по порядку при использовании назначенных инициализаторов.

M.M 24.11.2015 06:37

Обратите внимание, что пустой инициализатор агрегата также работает:

SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};

В ответ на вопрос, почему происходит сбой ShellExecuteEx(): ваша структура "sexi" SHELLEXECUTEINFO имеет много членов, и вы инициализируете только некоторые из них.

Например, член sexi.lpDirectory может указывать куда угодно, но ShellExecuteEx() все равно будет пытаться его использовать, поэтому вы получите нарушение доступа к памяти.

Когда вы включаете строку:

SHELLEXECUTEINFO sexi = {0};

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

Мне всегда было интересно, Почему вы должны использовать что-то вроде

struct foo bar = { 0 };

Вот пример для объяснения:

check.c

struct f {
    int x;
    char a;
} my_zero_struct;

int main(void)
{
    return my_zero_struct.x;
}

Я компилирую с помощью gcc -O2 -o check check.c, а затем выводю таблицу символов с помощью readelf -s check | sort -k 2 (это с gcc 4.6.3 в ubuntu 12.04.2 в системе x64). Отрывок:

59: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
48: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
25: 0000000000601018     0 SECTION LOCAL  DEFAULT   25 
33: 0000000000601018     1 OBJECT  LOCAL  DEFAULT   25 completed.6531
34: 0000000000601020     8 OBJECT  LOCAL  DEFAULT   25 dtor_idx.6533
62: 0000000000601028     8 OBJECT  GLOBAL DEFAULT   25 my_zero_struct
57: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT  ABS _end

Важной частью здесь является то, что my_zero_struct следует за __bss_start. Раздел ".bss" в программе на языке C - это раздел памяти, который установлен в ноль. перед. main называется, см. википедия на .bss.

Если вы измените приведенный выше код на:

} my_zero_struct = { 0 };

Тогда результирующий исполняемый файл "проверки" выглядит как точно, по крайней мере, с компилятором gcc 4.6.3 в ubuntu 12.04.2; my_zero_struct все еще находится в секции .bss, и поэтому он будет автоматически инициализирован нулем перед вызовом main.

Подсказки в комментариях, что memset может инициализировать «полную» структуру, также не является улучшением, потому что раздел .bss полностью очищен, что также означает, что «полная» структура установлена ​​в ноль.

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

Глобальные и статические переменные всегда инициализируются по умолчанию 0 или ctor по умолчанию. Но если вы объявите экземпляр f локально, вы можете получить другие результаты.

Logman 06.06.2018 14:27

{0} - это анонимный массив, в котором его элемент равен 0.

Используется для инициализировать один или все элементы массива с помощью 0.

например int arr [8] = {0};

В этом случае все элементы arr будут инициализированы как 0.

{0} не является анонимным массивом. Это даже не выражение. Это инициализатор.

Keith Thompson 06.09.2014 00:15

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

Для скалярных типов (арифметических и указательных типов) фигурные скобки не нужны, но явно разрешены. Цитата N1570 проект стандарта ISO C, раздел 6.7.9:

The initializer for a scalar shall be a single expression, optionally enclosed in braces.

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

Для нескалярных типов (структуры, массивы, объединения) {0} указывает, что элемент первый объекта инициализируется нулем. Для структур, содержащих структуры, массивы структур и т. д., Это применяется рекурсивно, поэтому первый скалярный элемент устанавливается в ноль, в зависимости от типа. Как и в любом инициализаторе, любые неуказанные элементы обнуляются.

Промежуточные скобы ({, }) можно не устанавливать; например, оба они действительны и эквивалентны:

int arr[2][2] = { { 1, 2 }, {3, 4} };

int arr[2][2] = { 1, 2, 3, 4 };

вот почему вам не нужно писать, например, { { 0 } } для типа, первый элемент которого не скалярен.

Итак, это:

some_type obj = { 0 };

- это сокращенный способ инициализации obj нулем, означающий, что каждому скалярному подобъекту obj присваивается значение 0, если это целое число, 0.0, если это число с плавающей запятой, или нулевой указатель, если это указатель.

Правила аналогичны для C++.

В вашем конкретном случае, поскольку вы присваиваете значения sexi.cbSize и т. д., Ясно, что SHELLEXECUTEINFO является типом структуры или класса (или, возможно, объединением, но, вероятно, нет), поэтому не все из этого применимо, но, как я сказал, { 0 } - это распространенная идиома, которую можно использовать в более общих ситуациях.

Это нет (обязательно) эквивалентно использованию memset для установки представления объекта на все биты-ноль. Ни 0.0 с плавающей запятой, ни нулевой указатель не обязательно представлены как все биты-ноль, и инициализатор { 0 } не обязательно устанавливает байты заполнения в какое-либо конкретное значение. Однако в большинстве систем это может иметь тот же эффект.

В C++ {0} не является допустимым инициализатором для объекта без конструктора, принимающего 0; ни для агрегата, первый элемент которого является таковым (или агрегата без элементов)

M.M 24.11.2015 05:49

Это синтаксический сахар для инициализации всей вашей структуры пустыми / нулевыми / нулевыми значениями.

Длинная версия

SHELLEXECUTEINFO sexi;
sexi.cbSize = 0;
sexi.fMask = 0;
sexi.hwnd = NULL;
sexi.lpVerb = NULL;
sexi.lpFile = NULL;
sexi.lpParameters = NULL;
sexi.lpDirectory = NULL;
sexi.nShow = nShow;
sexi.hInstApp = 0;
sexi.lpIDList = NULL;
sexi.lpClass = NULL;
sexi.hkeyClass = 0;
sexi.dwHotKey = 0;
sexi.hMonitor = 0;
sexi.hProcess = 0;

Укороченная версия

SHELLEXECUTEINFO sexi = {0};

Разве это не было намного проще?

Еще это приятно, потому что:

  • вам не нужно выслеживать каждого участника и инициализировать его
  • вам не нужно беспокоиться о том, что вы можете не инициализировать новых участников, когда они будут добавлены позже
  • вам не обязательно позвоните ZeroMemory

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