Почему мне не нужны 3 уровня скобок для инициализации 3 уровней массивов? - PullRequest
8 голосов
/ 08 февраля 2020

Я сталкивался с этим примером

struct sct
{
    int t[2];
};

struct str
{
    sct t[2];
};

int main()
{
    str t[2] = { {0, 2, 4, 6}, {1, 3, 5, 7} }; //Who does this work?

    cout << t[1].t[0].t[1] << t[0].t[1].t[0];     

    return 0;
}

Это компилируется и работает нормально. Он дает вывод 34

Я ожидал, что синтаксис для инициализации будет:

str t[2] = {  { {0, 2},{4, 6} }, { {1, 3},{5, 7} }   };

Вместо

 { {0, 2, 4, 6}, {1, 3, 5, 7} };

Но это дало:

In function 'int main()':
error: too many initializers for 'str'

Может кто-нибудь объяснить, почему?

Это изображение, иллюстрирующее то, как я вижу это: enter image description here

Как я должен думать, когда оно приходит инициализации вложенных структур?

1 Ответ

7 голосов
/ 08 февраля 2020

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

Сначала позвольте мне показать решение, которое, кажется, работает:

int main()
{
    str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };
    cout << t[1].t[0].t[1] << t[0].t[1].t[0] << endl;

    return 0;
}

Итак, мы иметь массив str, который содержит массив sct.

Давайте начнем с последнего. Вы инициализируете массив sct примерно так:

sct x[2] = { {0, 1}, {2, 3} };

Теперь для одного экземпляра str вы можете go с

str y = { { {0, 2}, {4, 6} } };

То, что остается для str t[2] состоит из двух копий str инициализирующих выражений в фигурных скобках:

str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };

Отредактировано: В первом чтении я неправильно понял вопрос. После того, как сообщение было обновлено, выяснилось, что вопрос в том, почему можно сбросить две пары скобок, но сброс одной пары приводит к синтаксической ошибке.

Чтобы понять, как синтаксический анализатор интерпретирует код, который вы Возможно, хотелось бы посмотреть на дерево разбора. Вы можете создавать дампы g cc на нескольких этапах парсера с опциями -fdump-tree-.... Здесь -fdump-tree-original может быть полезно.

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

struct sct
{
    int a[2];
};

struct str
{
    sct b[2];
};

Вот вывод, который я получил с G CC 7.5 из

>>>> CODE:
str t[2] = { { 0, 2, 4, 6 }, { 1, 3, 5, 7 } };
>>>> tree enabled by -tree-original
struct str t[2] = {{.b={{.a={0, 2}}, {.a={4, 6}}}}, {.b={{.a={1, 3}}, {.a={5, 7}}}}};

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

Теперь рассмотрим выражение, которое не в состоянии compile:

str t[2] = {  { {0, 2},{4, 6} }, { {1, 3},{5, 7} }   };

A верхним уровнем дерева для этого выражения будет

/*Step 1: */ struct str t[2] = { {.b={0, 2}, {4, 6} }, {.b={1, 3}, {5, 7} } };

Но так как b является массивом sct, мы пытаемся инициализировать его с помощью {0,2} Getting

sct b[2] = {0, 2};

Это расширяется до

struct sct b[2] = {{.a={0, 2} }};

Это допустимый C ++, так как первый элемент массива инициализируется явно, а второй элемент неявно инициализируется с нулями.

С этим знанием мы получаем следующее дерево

/*Step 2: */ struct str t[2] = { {.b={{.a={0, 2} }}, {4, 6} }, {.b={{.a={1, 3} }}, {5, 7} } };

Теперь нам осталось следующее:

 struct str z = { { { {0,2} }, { {0,0} } }, {4, 6} };

И компилятор справедливо жалуется: * 106 2 *

 error: too many initializers for ‘str’

В качестве окончательной проверки рассмотрим следующее объявление

 struct sxc
 {
     sct b[2];
     int c[2];
 }

 struct sxc z = { {0,2} , {4, 6} };

. Компилируется и получается следующая структура:

 { .b = { { .a={0,2} }, { .a={0,0} } }, .c={4, 6} }
...