Что означает {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);
}
Без этого приведенный выше код будет зависать во время выполнения.
9 ответов
То, что здесь происходит, называется агрегатной инициализацией. Вот (сокращенное) определение агрегата из раздела 8.5.1 спецификации ISO:
Агрегат - это массив или класс без объявленных пользователем конструкторов, без закрытых или защищенных нестатических элементов данных, без базовых классов и виртуальных функций.
Теперь, используя {0}
инициализировать агрегат, как это в основном трюк 0
все дело. Это связано с тем, что при использовании агрегатной инициализации вам не нужно указывать все элементы, а спецификация требует, чтобы все неопределенные элементы инициализировались по умолчанию, что означает 0
для простых типов.
Вот соответствующая цитата из спецификации:
Если в списке меньше инициализаторов, чем элементов в агрегате, то каждый элемент, не инициализированный явно, должен инициализироваться по умолчанию. Пример:
struct S { int a; char* b; int c; }; S ss = { 1, "asdf" };
инициализирует
ss.a
с1
,ss.b
с"asdf"
, а такжеss.c
со значением выражения формыint()
, то есть,0
,
Вы можете найти полную спецификацию на эту тему здесь
Следует помнить, что этот метод не устанавливает нулевые байты заполнения. Например:
struct foo
{
char c;
int i;
};
foo a = {0};
Это не то же самое, что:
foo a;
memset(&a,0,sizeof(a));
В первом случае байты pad между c и i неинициализированы. Почему тебя это волнует? Ну, если вы сохраняете эти данные на диск или отправляете их по сети или что-то еще, у вас может быть проблема безопасности.
Обратите внимание, что пустой инициализатор агрегата также работает:
SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};
В ответ на почему ShellExecuteEx()
сбой: ваш SHELLEXECUTEINFO
Структура "sexi" имеет много членов, и вы только инициализируете некоторые из них.
Например, член sexi.lpDirectory
может указывать куда угодно, но ShellExecuteEx()
по-прежнему будет пытаться использовать его, следовательно, вы получите нарушение доступа к памяти.
Когда вы включаете строку:
SHELLEXECUTEINFO sexi = {0};
перед остальными настройками структуры вы говорите компилятору обнулить все элементы структуры, прежде чем инициализировать конкретные, которые вас интересуют. ShellExecuteEx()
знает, что если sexi.lpDirectory
ноль, он должен игнорировать это.
{0}
является допустимым инициализатором для любого (завершенного объекта) типа, как в C, так и в C++. Это общая идиома, используемая для инициализации объекта в ноль (читайте дальше, чтобы увидеть, что это значит).
Для скалярных типов (арифметических и указательных типов) фигурные скобки не нужны, но они явно разрешены. Цитирую черновик N1570 стандарта ISO C, раздел 6.7.9:
Инициализатор для скаляра должен быть одним выражением, необязательно заключенным в фигурные скобки.
Инициализирует объект до нуля (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 }
инициализатор не обязательно устанавливает байты заполнения в какое-либо конкретное значение. Однако на большинстве систем это может иметь такой же эффект.
Я также использую его для инициализации строк, например.
char mytext[100] = {0};
Прошло некоторое время с тех пор, как я работал на c/ C++, но в IIRC такой же ярлык можно использовать и для массивов.
Я всегда задавался вопросом, почему вы должны использовать что-то вроде
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 };
Тогда полученный исполняемый файл "check" выглядит точно так же, по крайней мере, с компилятором gcc 4.6.3 в ubuntu 12.04.2; my_zero_struct
все еще в .bss
раздел и, таким образом, он будет автоматически инициализирован в ноль, до main
называется.
Подсказки в комментариях, что memset
может инициализировать "полную" структуру, также не является улучшением, потому что .bss
раздел очищен полностью, что также означает, что "полная" структура установлена в ноль.
Возможно, что стандарт языка C не упоминает ничего из этого, но в реальном C-компиляторе я никогда не видел другого поведения.
Это синтаксический сахар для инициализации всей вашей структуры пустыми / нулевыми / нулевыми значениями.
Длинная версия
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} - это анонимный массив, содержащий его элемент как 0.
Это используется для инициализации одного или всех элементов массива с 0.
например, int arr[8] = {0};
В этом случае все элементы arr будут инициализированы как 0.