Що означає {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.