Союз, хранящий несколько значений

Вот код:

#include <iostream>

union mytypes_t {
    char c;
    int i;
    float f;
    double d;
} mytypes;

int main() {

    mytypes.c = 'z';
    mytypes.d = 4.13021;
    mytypes.f = 41.7341;

    cout << mytypes.d << endl;
    return 0;
}

Программа выводит 4.13021 (значение, объявленное как double). Когда я пытаюсь вывести mytypes.c вместо этого он печатает пустой квадрат (указывая на то, что символ отображается неправильно).

Из того, что я понимаю об объединении, не должно ли оно содержать только одно значение, одного типа? Если это правда, разве это не будет float со значением 41.7341, и поэтому вызов его как double или char выдаст ошибку?

3 ответа

Решение

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

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

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

#include <iomanip>
#include <iostream>
#include <string.h>

union mytypes_t {
    unsigned char a[8];
    char c;
    int i;
    float f;
    double d;
} mytypes;

int main() {

    memset(&mytypes,0,8);

    std::cout << "Size of the union is: " << sizeof(mytypes) << std::endl;
    mytypes.c = 'z';
    for(int i=0;i<8;i++)
        printf("%02x ", mytypes.a[i]);
    printf("\n");

    mytypes.d = 4.13021;
    for(int i=0;i<8;i++)
        printf("%02x ", mytypes.a[i]);
    printf("\n");

    mytypes.f = 41.7341;
    for(int i=0;i<8;i++)
        printf("%02x ",mytypes.a[i]);
    printf("\n");

    std::cout << mytypes.c << std::endl;
    std::cout << mytypes.d << std::endl;
    std::cout << mytypes.f << std::endl;
    return 0;
}


Size of the union is: 8
7a 00 00 00 00 00 00 00 // The char is one byte
da 72 2e c5 55 85 10 40 // The double is eight bytes
b8 ef 26 42 55 85 10 40 // The float is the left most four bytes
�
4.13021
41.7341

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

Если вы явно приведете char к double или наоборот, получите ошибку, нет. Объединение - это в основном приведение типов и используется меньше памяти.

Я собираюсь сделать некоторые предположения о ваших типах, просто чтобы получить конкретные цифры для этого объяснения. Я предполагаю: char = 1 байт, int и float = 4 байта, double = 8 байтов.

Теперь они сохраняются в том же месте в памяти. Обычно, если вы объявляете int и float в структуре, int будет занимать первые 4 байта, а float будет занимать вторые 4 байта. Но сохраненные данные - это все 0 и 1, и сохранение того же числа, что и int или float, будет иметь разный порядок 0 и 1, потому что они по-разному интерпретируются системой. Например, int, float.

Когда вы сохраняете в одно из полей вашего союза, он принимает правильную последовательность нулей и единиц, которые соответствуют тому, что вы указали, и сохраняет его там. Если вы сохраните 15 в поле int и прочитаете его как число с плавающей точкой, вы получите совершенно другое число, так как вы заставили его читать число в другом порядке, чем это было запланировано.

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

Другие вопросы по тегам