Что такое ключевое слово union в C++?

Ответ

union — это составной тип данных в C++, все члены которого разделяют одну и ту же область памяти. Размер union равен размеру его наибольшего члена. В один момент времени union может содержать значение только одного из своих членов.

Основное применение: экономия памяти и низкоуровневая работа с данными (например, представление одних и тех же байтов разными типами).

#include <iostream>

union DataPacket {
    int32_t asInt;
    float asFloat;
    uint8_t asBytes[4];
};

int main() {
    DataPacket packet;
    packet.asFloat = 3.14159f;

    // Интерпретируем те же байты как целое число
    std::cout << "Float value: " << packet.asFloat << std::endl;
    std::cout << "Same bits as integer: 0x" << std::hex << packet.asInt << std::endl;

    // Доступ к отдельным байтам
    std::cout << "Bytes: ";
    for (int i = 0; i < 4; ++i) {
        std::cout << static_cast<int>(packet.asBytes[i]) << ' ';
    }
    std::cout << std::endl;

    return 0;
}

Важные замечания:

  • Активен только один член union. Чтение неактивного члена — это неопределенное поведение (UB).
  • В современном C++ (C++11 и выше) рекомендуется использовать объединения с вариантом (variant unions), где можно явно отслеживать активный член, или стандартный класс std::variant.
  • Часто используется в системном программировании, для работы с регистрами оборудования или разбора сетевых пакетов.

Ответ 18+ 🔞

Давай разберёмся с этим union, а то народ часто нихуя не понимает, зачем эта штука вообще нужна. Представь себе, что у тебя есть одна комната, но в неё можно поставить либо диван, либо кровать, либо шкаф. Всё сразу — никак, места хватит только на что-то одно. Вот union — это такая же комната в памяти, только для данных.

Короче, суть: это тип, где все переменные-члены делят одну и ту же память. Размер union'а равен размеру самого большого его члена. И в один момент времени там живёт значение только одного из них. Если записал в один член, а читаешь из другого — это пиздец, неопределённое поведение, ёпта. Компилятор тебя не остановит, но результат будет — хуй с горы.

Зачем это надо, спросишь? Ну, во-первых, чтобы память экономить, если тебе нужно хранить что-то одно из нескольких вариантов. Во-вторых, для всяких низкоуровневых фокусов — посмотреть на одни и те же байты под разными соусами.

#include <iostream>

union DataPacket {
    int32_t asInt;
    float asFloat;
    uint8_t asBytes[4];
};

int main() {
    DataPacket packet;
    packet.asFloat = 3.14159f;

    // Смотрим на те же байты, но как на целое число
    std::cout << "Float value: " << packet.asFloat << std::endl;
    std::cout << "Same bits as integer: 0x" << std::hex << packet.asInt << std::endl;

    // Лезем прямо в отдельные байты
    std::cout << "Bytes: ";
    for (int i = 0; i < 4; ++i) {
        std::cout << static_cast<int>(packet.asBytes[i]) << ' ';
    }
    std::cout << std::endl;

    return 0;
}

Важные моменты, которые надо помнить, а то будет хиросима:

  • Активен только один член. Записал в asFloat — работай с asFloat. Если сразу после этого начнёшь читать asInt — это чистой воды русская рулетка, неопределённое поведение. Может повезёт, а может и нет, ядрёна вошь.
  • В современном C++ (C++11 и дальше) для таких штук лучше использовать объединения с вариантом (variant unions), где можно явно указать, какой член сейчас активен, или вообще взять std::variant из стандартной библиотеки — он безопаснее и понятнее.
  • Где это реально применяется? Да в основном в системняке: разбор сетевых пакетов, работа с регистрами какого-нибудь железа, когда нужно одни и те же биты интерпретировать по-разному. В обычном прикладном коде — редкость, потому что доверия к этой конструкции ебать ноль, легко накосячить.