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

237

Когда {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 ответов

275
Лучший ответ

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

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

  • 14
    Отличный ответ. Просто хотел добавить, что инициализация агрегата с {0} - это то же самое, что инициализация его просто {}. Возможно, первое делает более очевидным, что встроенные типы обнуляются.
  • 6
    Некоторые компиляторы подавляются {}, поэтому {0} привыкает
Показать ещё 7 комментариев
87

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

struct foo
{
    char c;
    int  i;
};

foo a = {0};

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

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

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

  • 13
    Конечно, это проблема безопасности, только если вы пишете (f, & a, sizeof (a)), что может привести к разной кодировке файлов на разных процессорах / компиляторах. Хорошо отформатированный вывод будет безопасным без memset.
  • 3
    Кроме того, если вы отправляете данные по сети, вы всегда будете устанавливать выравнивание для упаковки. Таким образом вы получите как можно меньше дополнительных байтов заполнения.
Показать ещё 7 комментариев
18

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

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

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

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

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

SHELLEXECUTEINFO sexi = {0};

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

7

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

char mytext[100] = {0};
  • 5
    Хотя, конечно, если mytext используется строка, char mytext [100]; mytext [0] = '\ 0'; будет иметь тот же эффект, что и пустая строка, но только приведет к тому, что реализация обнулит первый байт.
  • 0
    @Chris: мне часто хотелось, чтобы был синтаксис для частичной инициализации объекта. Способность «объявлять и инициализировать» x, затем делать то же самое с y, затем с z, намного приятнее, чем необходимость объявлять x, y и z, а затем инициализировать x, y и z, но инициализировать 100 байтов, когда только на самом деле нужна инициализация, кажется довольно расточительной.
5

{0} является допустимым инициализатором для любого (полного объекта) типа, как в 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, если он является плавающей точкой, или нулевой указатель если это указатель.

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

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

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

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

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

2

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

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 };

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

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

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

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

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

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

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

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

  • 4
    {0} не является анонимным массивом. Это даже не выражение. Это инициализатор.
Сообщество Overcoder
Наверх
Меню