Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> Язык C

Объединения в C (union): эффективное использование памяти для разных типов данных

Что такое объединение (union)?

Объединение — пользовательский тип, позволяющий хранить разные типы данных в одной и той же области памяти. В любой момент времени активно только одно поле. Размер объединения = размер самого большого поля (с учётом выравнивания).

Объявление и определение

union Data {
    int i;
    float f;
    char str[20];
}; // размер = max(sizeof(int), sizeof(float), 20) = 20 байт (обычно выровнено до 24)

union Data data; // переменная объединения

Доступ к полям

Синтаксис как у структур: data.i, data.f, data.str. Но писать можно только в одно поле, читать — то же поле, в которое писали последним.

Пример: демонстрация перезаписи

union Data data;
data.i = 10;
printf("i = %d\n", data.i); // 10

data.f = 3.14f;
printf("f = %f\n", data.f); // 3.140000
// data.i теперь содержит биты 3.14f как int (мусорное значение)

strcpy(data.str, "Hello");
printf("str = %s\n", data.str); // Hello
// data.i и data.f теперь содержат части строки

Правильное использование: одно поле за раз

union Data data;
data.i = 42;
printf("int: %d\n", data.i); // 42

data.f = 2.71f;
printf("float: %f\n", data.f); // 2.71

strcpy(data.str, "C union");
printf("string: %s\n", data.str); // C union

Когда использовать union?

Tagged Union (дискриминированное объединение)

Комбинация enum (тег) + union для безопасного варианта:

enum ValueType { TYPE_INT, TYPE_FLOAT, TYPE_STRING };

struct Variant {
    enum ValueType type;
    union {
        int i;
        float f;
        char *s; // указатель на строку (память управляется отдельно)
    } value;
};

void print_variant(struct Variant *v) {
    switch (v->type) {
        case TYPE_INT:    printf("int: %d\n", v->value.i); break;
        case TYPE_FLOAT:  printf("float: %f\n", v->value.f); break;
        case TYPE_STRING: printf("string: %s\n", v->value.s); break;
    }
}

Анонимные объединения (C11)

Внутри структуры можно объявить union без имени — его поля становятся доступны напрямую:

struct Packet {
    int header;
    union {
        struct { int x, y; } mouse;
        struct { int key, mods; } keyboard;
    }; // анонимный union
    // доступ: pkt.mouse.x, pkt.keyboard.key
};

Type Punning (переинтерпретация типов) — осторожно!

Чтение из поля union, отличного от последнего записанного — implementation-defined (C99+C11 разрешают для union, но не для приведения указателей).

// Разрешено в C (не в C++!):
union { float f; uint32_t u; } converter;
converter.f = 1.0f;
uint32_t bits = converter.u; // биты IEEE 754 float как int

// НЕ ДЕЛАЙТЕ так (strict aliasing violation):
float f = 1.0f;
uint32_t bits = *(uint32_t*)&f; // UB! Компилятор может оптимизировать неправильно.

Выравнивание

Адрес union выравнивается по максимальному выравниванию его полей. _Alignof(union Data) (C11).

Сравнение struct vs union

Характеристикаstructunion
ПамятьСумма размеров полей + paddingМакс. размер поля + padding
Активные поляВсе одновременноТолько одно в данный момент
ИспользованиеАгрегация связанных данныхВариантность, экономия памяти, type punning
ИнициализацияВсе поля (или subset с нулем)Только первое поле (или designated)

Инициализация union

union Data d1 = { .i = 42 };           // designated (C99) — рекомендуется
union Data d2 = { 3.14f };             // инициализирует ПЕРВОЕ поле (f если float первым)
union Data d3 = { .str = "hello" };    // designated для любого поля

Пример: парсинг сетевого пакета

#pragma pack(push, 1) // без padding (GCC/Clang/MSVC)
struct PacketHeader {
    uint16_t magic;
    uint8_t version;
    uint8_t type;
    uint32_t payload_len;
};

union PacketPayload {
    struct { uint32_t user_id; char name[32]; } login;
    struct { uint32_t msg_id; char text[256]; } message;
    struct { uint32_t code; char reason[64]; } error;
    uint8_t raw[256]; // сырой доступ
};
#pragma pack(pop)

// Использование:
union PacketPayload payload;
payload.login.user_id = 12345;
strcpy(payload.login.name, "alice");

Частые ошибки

Язык C

  1. Микропроцессоры: эволюция вычислений с запрограммированной памятью
  2. Полные данные по ОУ: даташиты National Semiconductor и CA3130
  3. Понимание типов данных C++: основы, модификаторы и лучшие практики
  4. Типы данных в C: int, float, char и другие — полное руководство
  5. Объединения в C: определение, использование и сравнение со структурами
  6. Освоение типов данных в Python: практическое руководство
  7. Типы данных в C: полное руководство по целочисленным, вещественным и производным типам
  8. Структуры данных в C++: массивы, структуры и эффективное программирование
  9. Абстракция данных в C++: разделение интерфейса и реализации
  10. Инкапсуляция данных в C++: защита вашего кода