Что это означает, когда {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);
}
Без него приведенный выше код выйдет из строя во время выполнения.





То, что здесь происходит, называется инициализацией совокупность. Вот (сокращенное) определение агрегата из раздела 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.awith1,ss.bwith"asdf", andss.cwith the value of an expression of the formint(), that is,0.
Вы можете найти полную спецификацию по этой теме здесь
Некоторые компиляторы подавляются {}, поэтому {0} используется
Не совсем .. В C++, если первый член не может быть построен с нуля, {0} не будет работать. Пример: структура A {B b; int i; char c; }; структура B {B (); B (строка); }; A a = {}; // этот оператор нельзя переписать как 'A a = {0}'.
@Branan, это потому, что в C "{}" недопустимо. В C++ это так. @ don.neufeld, это изменилось с C++ 03 (инициализация по умолчанию -> инициализация значения). Имейте в виду, что цитата цитирует стандарт C++ 98.
Итак, заменяет ли это необходимость использования ZeroMemory () (в C++)?
@DebugErr Хотя есть много случаев, когда любой из них будет работать, остаются случаи, когда Microsoft ZeroMemory или стандартный memset не могут работать (в основном, для типов, не относящихся к POD), а также случаи, когда агрегированная инициализация не может работать (в основном, для массивов, где размер не известен статически).
Стандартные цитаты @DonNeufeld должны сопровождаться названием цитируемого стандарта; в данном случае это очень актуально, потому что вы процитировали C++ 98, но C++ 03 изменил инициализированный по умолчанию на инициализированное значение. C++ 03 также изменил значение терминов; инициализированный по умолчанию для примитивных типов в C++ 98 означает все биты-ноль, но в C++ 03 означает неинициализированный. Более поздние стандарты (после того, как вы написали свой ответ) следуют модели C++ 03.
@Aaron не уверен, что указание конструктора для B по-прежнему будет работать. поскольку он может лишить граф объектов права называться агрегатным типом. (или я ошибаюсь?)
Прошло некоторое время с тех пор, как я работал с c / C++, но IIRC, тот же ярлык можно использовать и для массивов.
Я также использую его для инициализации строк, например.
char mytext[100] = {0};
Хотя, конечно, если mytext используется как строка, char mytext [100]; mytext [0] = '\ 0'; будет иметь тот же эффект, что и пустая строка, но приведет к обнулению реализации только первого байта.
@Chris: Мне часто хотелось иметь синтаксис для частичной инициализации объекта. Возможность «объявить и инициализировать» x, а затем сделать то же самое с y, а затем z, намного лучше, чем необходимость объявлять x, y и z, а затем инициализировать x, y и z, но инициализировать 100 байтов только тогда, когда действительно нужна инициализация, кажется довольно расточительной.
Следует помнить, что этот метод не устанавливает байты заполнения в ноль. Например:
struct foo
{
char c;
int i;
};
foo a = {0};
Не то же самое, что:
foo a;
memset(&a,0,sizeof(a));
В первом случае байты заполнения между c и i не инициализируются. Зачем тебе это нужно? Что ж, если вы сохраняете эти данные на диск или отправляете их по сети или что-то еще, у вас может быть проблема с безопасностью.
Конечно, проблема безопасности возникает только в том случае, если вы «пишете (f, & a, sizeof (a))», что может привести к разной кодировке файлов на разных процессорах / компиляторах. Хорошо отформатированный вывод был бы безопасен без memset.
Кроме того, если вы отправляете материал по сети, вы всегда устанавливаете выравнивание для упаковки. Таким образом вы получите как можно меньше дополнительных байтов заполнения.
Следует отметить, что, хотя спецификация не требует инициализации заполнения, любой нормальный компилятор это сделает, поскольку инициализация "вокруг" всего лишь требует времени.
Я хотел бы оспорить использование слов «не является». Байты заполнения определяются реализацией. Компилятор может по своему усмотрению превратить foo a = {0} в memset (& a, 0, sizeof (a)). Не требуется «пропускать» байты заполнения, и Только устанавливает foo.c и foo.i. +1 за (потенциальную) ошибку безопасности
@HaroldEkstrom обновление этого ответа: как в C11, так и в C++ 11, биты заполнения явно должны быть установлены в ноль этим методом.
@Leushenko у вас есть ссылка на это? Насколько мне известно, это верно только для объектов со статической продолжительностью хранения.
@ M.M на самом деле вы правы, явная ссылка на заполнение находится в разделе о статических объектах; однако «остальная часть агрегата должна быть неявно инициализирована так же, как объекты, которые имеют статическую продолжительность хранения» (C11 6.7.9). Я предполагаю, что технически вы могли бы повернуть это так, чтобы это означало, что биты заполнения пропускаются. если только вы предоставляете явные инициализаторы для всех членов, но это потребует от поставщика компилятора изо всех сил, чтобы троллить вас. Неоднозначный текст, но я думаю, что намерение состоит в том, чтобы обнулить отступы.
В пункте 19 @Leushenko говорится, что «все подобъекты, которые не инициализированы явно», поэтому я бы склонился к пункту 21 (который вы цитировали), будучи небрежным. Было бы поистине странно, если бы спецификация позволяла неинициализировать заполнение до момента последнего инициализатора, после которого заполнение должно было быть обнулено, особенно учитывая, что инициализаторы могут появляться не по порядку при использовании назначенных инициализаторов.
Обратите внимание, что пустой инициализатор агрегата также работает:
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 локально, вы можете получить другие результаты.
{0} - это анонимный массив, в котором его элемент равен 0.
Используется для инициализировать один или все элементы массива с помощью 0.
например int arr [8] = {0};
В этом случае все элементы arr будут инициализированы как 0.
{0} не является анонимным массивом. Это даже не выражение. Это инициализатор.
{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; ни для агрегата, первый элемент которого является таковым (или агрегата без элементов)
Это синтаксический сахар для инициализации всей вашей структуры пустыми / нулевыми / нулевыми значениями.
Длинная версия
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
Отличный ответ. Просто хотел добавить, что инициализация агрегата с помощью {0} аналогична его инициализации с помощью простого {}. Возможно, первое делает более очевидным обнуление встроенных типов.