Подтвердить что ты не робот

C: Инициализация переменных структуры после объявления

Я столкнулся с этим в последнее время, но не мог понять, почему язык позволит b = c; ниже и сбой b = {3, 4}. Есть ли проблема с допуском последнего?

struct T {
    int x;
    int y;
};

int main()
{
    T a = {1, 2};
    T b;

    b = {3, 4}; // why does this fail ?

    T c = {3, 4};
    b = c; // this works

    return 0;
}
4b9b3361

Ответ 1

Он терпит неудачу, потому что {3, 4}, хотя он действительный инициализатор, не является выражением (по крайней мере, он не находится в C, см. ниже, чтобы узнать больше о С++).

Каждое выражение в C имеет тип, который можно определить, исследуя само выражение. {3, 4} потенциально может иметь тип struct T или int[2] (тип массива) или любое из множества других типов.

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

b = (struct T){3, 4};

Обратите внимание, что (struct T) не является оператором трансляции; это часть синтаксиса составного литерала.

Дополнительную информацию о составных литералах см. в разделе 6.5.2.5 проекта проекта стандарта C11.

Компонентные литералы были введены в 1999 году стандартом ISO C (C99). Если ваш компилятор не поддерживает C99 или лучше (* cough * Microsoft * cough *), вы не сможете их использовать.

Если вы используете С++ (который, не забывайте, это другой язык), он не поддерживает сложные литералы, но может быть и альтернатива. Как отмечает Potatoswatter в комментарии, это:

b = T{3, 4};

действителен в С++ 11 (но не в более ранних версиях языка С++). Это описано в разделе 5.2.3 [expr.type.conf] стандарта С++.

В этом отношении:

b = {3, 4};

также является синтаксисом С++ 11. Эта форма может использоваться в ряде определенных контекстов, включая правую часть задания. Это описано в разделе 8.5.4 [dcl.init.list] стандарта С++.

Один недавний проект стандарта С++ - N3485.

(g++ поддерживает сложные литералы C99 в С++ в качестве расширения.)

И если вы застряли с компилятором pre-C99, вы всегда можете написать свою собственную функцию инициализации, например:

struct T init_T(int x, int y) {
    struct T result;
    result.x = x;
    result.y = y;
    return result;
}

/* ... */

struct T obj;
/* ... */
obj = init_T(3, 4);

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

struct T obj;
/* ... */
{
    struct T tmp = { 3, 4 };
    obj = tmp;
}

Что лучше, вероятно, зависит от того, как структурирована ваша программа.

Ответ 2

Поскольку вы не использовали правильную нотацию C99 или C11 "составной литерал":

b = (struct T){ 3, 4 };

Более подробную информацию (среди других мест) см. в разделе 6..5.2 Операторы Postfix и §6.5.2.5 Составные литералы в ISO/IEC 9899: 2011.

( type-name ) { initializer-list }
( type-name ) { initializer-list , }

Ответ 3

Потому что это определение языка... Я не думаю, что есть какая-то особая причина, кроме "это затрудняет запись компилятора, чтобы разрешить это".

Вы можете сделать b = T{3, 4}, если у вас есть компилятор С++ 11.