Що означає {0} при ініціалізації об'єкта?


252

Коли {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);
}

Без цього вищезазначений код вийде з ладу під час виконання.

Відповіді:


302

Те, що відбувається тут, називається сукупною ініціалізацією. Ось (скорочене) визначення агрегату з розділу 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.

Ви можете знайти повну специфікацію на цю тему тут


15
Відмінна відповідь. Просто хотів додати, що ініціалізація сукупності з {0} - це те саме, що ініціалізувати її просто {}. Можливо, перший робить більш очевидним, що вбудовані типи отримують нуль.
— Джеймс Хопкін

7
Деякі компілятори задихаються {}, тому {0} звикає
— Branan

10
Не зовсім .. У C ++, якщо перший член не може бути побудований на нулі, тоді {0} не буде працювати. Наприклад: структура A {B b; int i; char c; }; структура B {B (); Б (рядок); }; A a = {}; // це твердження не можна переписати як "A a = {0}".
— Аарон

25
@Branan, це тому, що в C "{}" не вірно. У C ++ це. @ don.neufeld, це змінилося з C ++ 03 (ініціалізовано за замовчуванням -> значення-ініціалізовано). Цитата цитує стандарт C ++ 98, майте на увазі.
— Йоханнес Шауб - ліб

3
Отже, чи замінює це необхідність використання ZeroMemory () (у VC ++)?
— Рей

89

Потрібно пам’ятати, що ця техніка не встановлюватиме байти для забивання на нуль. Наприклад:

struct foo
{
    char c;
    int  i;
};

foo a = {0};

Не те саме, що:

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

У першому випадку байти прокладки між c і i не ініціалізуються. Чому б ти хвилювався? Добре, якщо ви зберігаєте ці дані на диску або надсилаєте їх по мережі чи іншому, у вас може виникнути проблема із безпекою.


13
Звичайно, це лише проблема безпеки, якщо ви пишете (f, & a, sizeof (a)) ', яка може створювати різні кодування файлів у різних процесорах / компіляторах. Добре відформатований вихід був би безпечним без мемсета.
— Аарон

3
Крім того, якщо ви надсилаєте речі по мережі, ви завжди встановлюватимете вирівнювання, яке потрібно запакувати. Таким чином ви отримуєте якнайбільше зайвих байтів для забивання.
— Марк Кегель

18
Слід зазначити, що хоча специфікація не вимагає ініціалізації прокладки, будь-який розумний компілятор буде потребувати, оскільки це коштує лише часу на ініціалізацію "навколо".
— Томаш

4
Я хотів би взяти питання з використанням слів "немає". Байти прокладки визначені реалізацією. Компілятор вільний перетворити foo a = {0} у memset (& a, 0, sizeof (a)) на своєму власному дозвіллі. Не потрібно "пропускати" байти прокладки і встановлювати лише foo.c і foo.i. +1 для (потенційної) помилки безпеки через
— SecurityMatt

3
У пункті 19 Левшенка сказано, що "всі суб'єкти, які не ініціалізовані явно", тож я схиляюся до пункту 21 (який ви цитували) як неохайний. Було б по-справжньому химерним, якби специфікація дозволила продемонструвати прокладку до ініціалізації до моменту останнього ініціалізатора, після чого прокладка повинна бути нульова, особливо зважаючи на те, що ініціалізатори можуть з’являтися з ладу, коли використовуються призначені ініціалізатори.
— ММ

20

Зауважте, що пустий ініціалізатор агрегату також працює:

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

11

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

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

Коли ви включаєте рядок:

SHELLEXECUTEINFO sexi = {0};

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


7

Я також використовую його для ініціалізації рядків, наприклад.

char mytext[100] = {0};

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

@Chris: Мені часто хотілося, щоб був синтаксис часткової ініціалізації об'єкта. Бути в змозі "оголосити та ініціалізувати" x, потім зробити так само з y, тоді z, набагато приємніше, ніж оголосити x, y і z, а потім ініціалізувати x, y і z, але ініціалізувати 100 байт, коли тільки реально потрібна ініціалізація здається досить марною.
— supercat

7

{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 }ініціалізатор не обов'язково встановлює байти padding на якесь конкретне значення. Однак для більшості систем це може мати той же ефект.


1
У C ++ {0}не є дійсним ініціалізатором для об'єкта, який не приймає конструктор 0; ні для агрегату, перший елемент якого є таким (або агрегат без елементів)
— ММ

3

З часу, коли я працював у c / 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 називається см вікіпедії на .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

Синтатичний цукор ініціалізувати всю вашу структуру до порожніх / нульових / нульових значень.

Довга версія

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

-5

{0} - анонімний масив, що містить його елемент як 0.

Це використовується для ініціалізації одного або всіх елементів масиву з 0.

наприклад, int arr [8] = {0};

У цьому випадку всі елементи arr будуть ініціалізовані як 0.


4
{0}не є анонімним масивом. Це навіть не вираз. Це ініціалізатор.
— Кіт Томпсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.