Союз, хранящий несколько значений
Вот код:
#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 по-прежнему является числом, но не той, о которой вы думаете. Вполне допустимо считать это число и делать с ним вычисления. Вот почему что-то, использующее объединение, обычно имеет второе поле, часто перечисление, определяемое отдельно от объединения, указывающее, какой тип сохраняется в нем, чтобы вы знали, какой читать, когда другая часть программы использует его позже.