Есть ли какие-либо применения для нетипизированных объединений (без тега типа)? - PullRequest
1 голос
/ 09 декабря 2011

Кроме трюков приведения в память, есть ли способ использовать непомеченный союз

(тип данных, который явно содержит один из набора типов, который не является теговым объединением,

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

без связанного тега типа в контейнере, в котором он содержится?

Есть ли еще какое-то преимущество, которое объединение без тегов имеет над типизированным объединением?

edit: показать, что я имею в виду пример тегового объединения в haskell

data U = I Int | S String

вручную помеченный союз в c

enum u_types {INT,STRING};
typedef struct {
    u_types tag;
    union u{
    int i;
    char s[STRING_BUFFER_SIZE];
    } d;
}tagged union;

без маркировки в c

    union u{
    int i;
    char s[STRING_BUFFER_SIZE];
    } d;

Ответы [ 5 ]

7 голосов
/ 09 декабря 2011

Одно использование для непомеченных союзов - это легкий доступ к меньшим частям большего типа:

union reg_a {
  uint32_t    full;
  struct {    /* little-endian in this example */
    uint16_t  low;
    uint16_t  high;
  } __attribute__((__packed__));
};
union reg_a a;
a.full = 0x12345678; /* set all whole 32-bits */
a.high = 0xffff;      /* change the upper 16-bits */

union pix_rgba {
  uint32_t    pix;    /* to access the whole 32-bit pixel at once */
  struct {
    uint8_t   red;    /* red component only */
    uint8_t   green;  /* green component only */
    uint8_t   blue;   /* blue only */
    uint8_t   alpha;  /* alpha only */
  } __attribute__((__packed__));
};

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

1 голос
/ 09 декабря 2011

То, что вы разместили как «помеченное вручную», не является допустимым синтаксисом C, я полагаю, вы имели в виду:

typedef enum {INT,STRING} u_types;

typedef struct {
    u_types tag;
    union u{
    int i;
    char s[1];
    } d;
}tagged_union;

Обратите внимание, что формальное определение тега struct / union в C - это имя после ключевого слова struc / union. Во втором примере u - это тег union . Это то, что меня сильно смутило.


То, что вы описываете как «помеченное объединение», в компьютерной науке известно как вариант : переменная, которая может содержать несколько типов. Варианты обычно не одобряются в программировании в целом и в C в частности. Они запрещены в MISRA-C: 2004, правила 18.3 и 18.4.

Языки с поддержкой вариантов, таких как VB (и, возможно, Haskell?), Обычно представлены тогда как: эта переменная может содержать что угодно, но вы должны быть осторожны с ее использованием, поскольку она очень неэффективна.

В C варианты не только неэффективны, но и представляют угрозу безопасности. MISRA-c признает это в правиле 18.3:

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


Таким образом, вопрос, скорее, должен быть: есть ли применение для теговых союзов (вариантов)? Нет, нет. Я не использовал ни одной программы на C, которую когда-либо писал, для них нет смысла. Поскольку C имеет пустые указатели, в C есть гораздо лучшие и более безопасные способы создания общих типов данных:

void ADT_add_generic_type (void* data, some_enum_t type, size_t size);

Посмотрите, как стандарт C реализует функции qsort () и bsearch (), для некоторых хороших примеров общего программирования на C (ISO 9899: 1999 7.20.5.1):

void *bsearch (const void *key, 
               const void *base, 
               size_t nmemb, 
               size_t size,
               int (*compar)(const void *, const void *));

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


Однако для «нетегированных» союзов используется несколько. Протоколы данных, упаковка, доступ к аппаратному регистру и т. Д. И т. Д. Хороший пример приведен в ответе Дмитрия.

1 голос
/ 09 декабря 2011

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

Например:

union
{
  int x;
  int y;
} u;

void test(void)
{
  u.x = 10;
}

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

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

0 голосов
/ 09 декабря 2011

Везде, где у вас есть общая реализация, использующая void *, вы можете вместо этого использовать объединение без тегов. Поскольку вы используете void *, истинный тип объекта должен быть известен из контекста.

Это максимально переносимый способ реализации общей структуры данных, которая может хранить, например, union { void *ptr; unsigned x; } (на платформах C, где нет uintptr_t).

0 голосов
/ 09 декабря 2011

Вот хитрость:

static __attribute__((const, always_inline))
int32_t floatToIntBits(float f) {
    union {
        float value;
        int32_t bits;
    };
    value = f;
    return bits;
}
...