Преимущества использования union, когда то же самое можно сделать с помощью struct - C - PullRequest
6 голосов
/ 28 июля 2010

Мне трудно понять, как использовать union в C. Я читал здесь много постов на SO по этому вопросу. Но никто из них не объясняет, почему union предпочтительнее, когда то же самое можно достичь с помощью структуры.

Цитата из K & R

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

union u_tag {
      int ival;
      float fval;
      char *sval;
} u;

Использование будет

if (utype == INT)
    printf("%d\n", u.ival);
if (utype == FLOAT)
    printf("%f\n", u.fval);
if (utype == STRING)
    printf("%s\n", u.sval);
else
    printf("bad type %d in utype\n", utype);

То же самое можно реализовать с помощью структуры. Что-то вроде

struct u_tag {
    utype_t utype;
    int ival;
    float fval;
    char *sval;
} u;

if (u.utype == INT)
    printf("%d\n", u.ival);
if (u.utype == FLOAT)
    printf("%f\n", u.fval);
if (u.utype == STRING)
    printf("%s\n", u.sval);
else
    printf("bad type %d in utype\n", utype);

Разве это не то же самое? Какое преимущество дает union

Есть мысли?

Ответы [ 11 ]

9 голосов
/ 28 июля 2010

В примере, который вы разместили, размер объединения будет равным размеру с плавающей точкой (при условии, что он самый большой - как указано в комментариях, он может варьироваться в 64-битном компиляторе), тогда как размер структуры будетбыть суммой размеров float, int, char * и utype_t (и padding, если есть).

Результаты на моем компиляторе:

union u_tag {
    int ival;
    float fval;
    char *sval;
};
struct s_tag {
    int ival;
    float fval;
    char *sval;
};

int main()
{
    printf("%d\n", sizeof(union u_tag));  //prints 4
    printf("%d\n", sizeof(struct s_tag)); //prints 12
    return 0;
}
8 голосов
/ 28 июля 2010

Союзы могут быть использованы, если одновременно требуется доступ не более чем к одному члену. Таким образом, вы можете сэкономить память вместо использования структуры.

Существует четкий «обман», который может быть возможен с профсоюзами: запись одного поля и чтение из другого, проверка битовых комбинаций или их интерпретация по-разному.

4 голосов
/ 28 июля 2010

Союзы используются для сохранения только одного типа данных одновременно. Если значение переназначено, старое значение перезаписывается и не может быть доступно. В вашем примере члены int, float и char могут иметь разные значения в любое время при использовании в качестве структуры. Дело не в союзе. Так что это зависит от требований вашей программы и дизайна. Проверьте эту статью о том, когда использовать союз. Google может дать еще больше результатов.

4 голосов
/ 28 июля 2010

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

2 голосов
/ 28 июля 2010

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

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

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

В последнее время стоимость памяти была чрезвычайно низкой. Введение типа bool (и даже до этого, переменных int) позволило программисту 32-битных систем использовать 32 бита для представления двоичного состояния. Это часто встречается в программировании, хотя программист может использовать маски и получать 32 значения true / false в переменную.

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

1 голос
/ 28 июля 2010
У

союзов есть два основных применения:

Во-первых, как вы указали, сначала нужно указать тип варианта.В отличие от подхода struct, существует одна единица памяти, разделяемая между всеми членами объединения.Если память не проблема, структура также будет выполнять эту функцию.

Я обычно встраиваю объединение в структуру - структура гарантирует, что тип и данные хранятся вместе, а объединение означает, что сохраняется только одно значение.

struct any_tag {
    utype_t utype;
    union {
        int ival;
        float fval;
        char *sval;
    } u;
} data;

Второе, aUnion имеет большое применение для низкоуровневого доступа к необработанным данным - переосмысление одного типа как другого.Я использовал это для чтения и записи двоично-закодированных данных.

float ConvertByteOrderedBufferTo32bitFloat( char* input ) {
union {
    float f;
    unsigned char buf[4];
} data;

#if WORDS_BIGENDIAN == 1
data.buf[0] = input[0];
data.buf[1] = input[1];
data.buf[2] = input[2];
data.buf[3] = input[3];
#else
data.buf[0] = input[3];
data.buf[1] = input[2];
data.buf[2] = input[1];
data.buf[3] = input[0];
#endif

return dat1.f;
}

Здесь вы можете записывать отдельные байты, в зависимости от порядкового номера платформы, а затем интерпретировать эти 4 необработанных байта как IEEEплавать.Преобразование этого массива char в плавающее не будет иметь тот же результат.

1 голос
/ 28 июля 2010

Использование объединений для экономии памяти в современных системах в основном не выполняется, поскольку код для доступа к члену объединения быстро займет больше места (и будет медленнее), чем просто добавление другой переменной размера слова в память. Однако, когда ваш код должен поддерживать несколько архитектур с разными порядковыми номерами (например, что за слово), объединения могут быть полезны. Я предпочитаю использовать служебную библиотеку с порядком байтов (функции от до ), но некоторые люди любят союзы.

Аппаратные регистры с отображением в памяти также обычно доступны через объединения. Битовые поля в C (не используйте их, они имеют в виду) могут быть переданы как слова, используя объединения.

0 голосов
/ 02 июля 2011

Пример будет иметь смысл здесь. Смотрите пример ниже:

union xReg
{
    uint allX;
    struct
    {
        uint x3      : 9;
        uint x2      : 9;
        uint x1      : 14;
    };
};

uint является typedef без знака int.

Здесь это объединение представляет 32-битный регистр. Вы можете прочитать регистр, используя allX, а затем манипулировать им, используя структуру.

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

0 голосов
/ 29 июля 2010

Союзы сложны. В течение многих лет я не мог их понять, затем я начал делать что-то с сетевыми протоколами, и кто-то показал мне свет. Скажем, у вас есть заголовок, а затем после заголовка есть различные типы пакетов, например:

| тип (4 байта) | UID (8 байт) | длина полезной нагрузки (2 байта) | Полезная нагрузка (переменная длина) |

А потом были бы различные типы полезных нагрузок пакета ... Ради аргумента, могли бы быть привет, до свидания, и пакеты сообщения ...

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

struct packet {
  uint type;
  char unique_id [8];
  ushort payload_length;
  union payload {

    struct hello {
      ushort version;
      uint status;
    };

    struct goodbye {
      char reason[20];
      uint status;
    };

    struct message {
      char message[100];
    };

  };
};

Неизбежно, вы получаете этот протокол из операционной системы через вызов read (), и это всего лишь набор байтов. Но если вы осторожны с определением структуры, и все типы имеют правильный размер, вы можете просто сделать указатель на структуру, указать ее в буфере, заполненном случайными данными, и ...

char buf[100];
packet *pkt;
read(outsideworld,&buf,1000);
pkt = (struct packet *)&buf;

и читать ваши пакеты так же просто, как ...

switch(pkt->type){

  case PACKET_MESSAGE:
    printf("message = %s\n",
           pkt->payload.message.message);
    break;

  case PACKET_HELLO:
    printf("hello! version = %d status = %d\n",
           pkt->payload.hello.version,
           pkt->payload.hello.status);
    break;
  case PACKET_GOODBYE:
    printf("goodbye! reason = %s status = %d\n",
           pkt->payload.goodbye.reason,
           pkt->payload.goodbye.status);
    break;
}

Никаких обходов, подсчета байтов и т. Д. Вы можете вкладывать это так глубоко, как хотите (создайте объединение для IP-адресов, которое дает вам целые данные в виде целого числа без знака или отдельных байтов, чтобы было проще выведите из него 192.168.0.1).

Профсоюзы не замедляют ваш код, потому что все это просто переводится в машинные коды в смещения.

0 голосов
/ 28 июля 2010

заимствование из опубликованной вами цитаты "... любого из одного из нескольких типов ..." членов профсоюза одновременно.Это именно то, что такое союз;в то время как члены структуры могут быть назначены и доступны одновременно.

Объединение имеет больше смысла в выполнении некоторых программ системного уровня (ОС), таких как обмен данными между процессами / обработка параллелизма.

...