Зачем нам объявлять переменную типа union, если она вложена в структуру в C? - PullRequest
0 голосов
/ 15 марта 2020

У меня есть пример кода из учебника, в котором написано

struct goods {
    char name[20];
    union quantity {
        int count;
        float weight, volume;
    } q;
};

Я не могу понять, зачем нам объявлять переменную 'q' вместе с именем типа объединения 'amount'? Почему мы не можем получить просто «количество» и затем получить доступ к структурным полям через точку?


Обновление: правильно ли, что «количество» является именем / тегом объединения типов, тогда как « q '- это не переменная, а имя члена / поля объединения, который содержит подэлементы (количество, вес, объем)?

Ответы [ 2 ]

3 голосов
/ 15 марта 2020

Не ясно, какой конкретный c вопрос вы задаете об этом коде, поэтому давайте рассмотрим проблемы.

Члены структур

Когда внутри * появляется объявление union struct объявление, обычно это объявление члена той структуры, которая является объединением. Этот элемент является частью структуры так же, как и любой другой элемент, например int x, объявленный в структуре. Каждый экземпляр структуры содержит экземпляр каждого из его членов, включая объединение - объединение является частью структуры, а не отдельной вещью.

Имена

В этом коде:

    union quantity {
        int count;
        float weight, volume;
    } q;

идентификатор quantity является тегом для объединения. В этой роли он должен появляться после ключевого слова union, всегда как union quantity. Он только называет объединение тип ; он не называет объединение объекта или члена структуры. (Один и тот же идентификатор может использоваться в нескольких ролях. Мы могли бы также добавить объявление, которое определило quantity как тип, объект или член, и тогда у него было бы две роли: его можно использовать как union quantity для ссылки на тип объединения, и он может использоваться сам по себе для ссылки на то, что объявлено другим объявлением.)

В том же коде выше, q - это имя члена структура. Это имя для этого union quantity объекта, который есть в каждом экземпляре struct goods.

. В этом объявлении, если мы определим struct goods G;, тогда G.q ссылается на union quantity, который в G, а G.q.count, G.q.weight и G.q.volume относятся к членам в союзе G.q. (Одновременно может храниться только один из этих членов, поскольку все они перекрываются в объединении.)

Анонимные союзы

В C 2011 была добавлена ​​новая функция. union или structure может быть объявлено внутри другого union или structure без имени элемента:

struct goods {
    char name[20];
    union {
        int count;
        float weight, volume;
    };
};

Это не меняет структуру структуры вообще - она ​​все еще имеет те же члены. Однако их имена разные. Учитывая struct goods G, мы можем сослаться на член count как G.count вместо G.q.count и аналогично для weight и volume. (Обратите внимание, что в дополнение к удалению имени элемента q в этом коде также был удален тег quantity. В стандарте C есть правило, согласно которому структура или объединение должны быть анонимными. иметь тег, а также не иметь имя члена. Я не вижу технической причины для этого. Возможно, это был выбор, чтобы избежать ошибок, когда имена членов непреднамеренно опущены.)

Относительно того, почему кто-то может дайте члену профсоюза имя, а не сделайте его анонимным, одной из причин является то, что код был написан до 2011 года или после 2011 года, но должен использоваться в реализациях C, которые еще не поддерживали анонимных членов. Другая причина заключается в том, что они хотели различить guish членов объединения, поэтому любой, кто читает или пишет код, будет предупрежден о том, что эти члены находятся внутри чего-то внутри структуры, а не обычные прямые члены структуры.

0 голосов
/ 15 марта 2020

Я не могу понять, зачем нам объявлять переменную 'q' вместе с именем типа объединения 'количество'?

Как указано в вопросе, struct goods - это тип структуры с двумя членами: массив из 20 char, обозначенный name, и union quantity, обозначенный q (так что да, quantity является тегом объединения, а не именем члена). Нет необходимости в абсолютном смысле декларировать это таким образом, но такое объявление предоставляет несколько характеристик, которых нет у других альтернатив. Однако поймите, что, как заявлено в примере, count, weight и volume являются , а не членами struct goods. Скорее, они являются членами q, союза, который является членом struct goods.

Почему мы не можем получить просто «количество» и затем получить доступ к полям структуры через точку?

Потому что это не одна из альтернатив, которые предоставляет синтаксис C. В списке членов объявления типа структуры тег объединения (quantity в данном случае) может появляться только в объявлении члена с именем , поэтому, если это предусмотрено, вы также должны объявить идентификатор для объединения - q в примере. И объявив объединение в качестве именованного члена, вы должны получить доступ к его членам через идентификатор объединения.

С другой стороны, вы можете опустить тег, и если вы это сделаете, то При желании вы также можете опустить идентификатор объединения. Если вы опускаете идентификатор (и только в этом случае), у вас есть «анонимный член объединения», чьи собственные члены доступны , как если бы они действительно были членами содержащей структуры. Это очень близко к тому, о чем вы спрашиваете.

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

При этом различные варианты имеют некоторые различия в своих характеристиках. Прежде всего, оцените, что все эти формы имеют двоякое значение: они объявляют объединение тип , и они объявляют член структуры этого типа. Это важно, потому что если вы предоставите тег, вы можете объявить другие объекты того же типа объединения, где бы ни было объявление объединения. Кроме того, эта область не ограничивается объявлением типа структуры, которое содержит его, поэтому с представленной декларацией можно сделать что-то вроде этого:

void set_quantity(struct goods *g, union quantity quant) {
    g->q = quant;
}

Это невозможно для нетегированных объединений.

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

struct goods2 {
    char name[20];
    union {
        int count;
        float weight, volume;
    } q;
};

void copy_quantity(struct goods2 *dest, struct goods2 *src) {
    dest->q = src->q;
}

Мало того, что вы не можете сделать это с анонимным членом профсоюза, вы не можете сделать что-либо надежно эквивалентное. В частности, даже если вы готовы испытать неэффективность, связанную с копированием src->count, src->weight и src->volume по отдельности, несмотря на то, что только один из них действительно содержит значение, C не дает никаких обещаний, что при этом в любом порядке позволит надежно достичь желаемого результата.

...