Что означает {0} при инициализации объекта? - PullRequest
247 голосов
/ 18 сентября 2008

Когда {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 ]

298 голосов
/ 18 сентября 2008

То, что здесь происходит, называется агрегатная инициализация. Вот (сокращенно) определение агрегата из раздела 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.

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

88 голосов
/ 18 сентября 2008

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

struct foo
{
    char c;
    int  i;
};

foo a = {0};

Не совпадает с:

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

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

20 голосов
/ 30 сентября 2008

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

SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};
11 голосов
/ 09 апреля 2009

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

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

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

SHELLEXECUTEINFO sexi = {0};

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

7 голосов
/ 06 сентября 2014

{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 } не обязательно устанавливает байты заполнения в какое-либо конкретное значение. Однако в большинстве систем он может иметь такой же эффект.

7 голосов
/ 18 сентября 2008

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

char mytext[100] = {0};
3 голосов
/ 18 сентября 2008

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

2 голосов
/ 14 мая 2013

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

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, см. wikipedia на .bss .

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

} my_zero_struct = { 0 };

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

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

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

0 голосов
/ 05 сентября 2014

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

Используется для инициализации одного или всех элементов массива с 0.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...