Reputation:
Here is the code:
#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;
}
The program outputs 4.13021 (the value declared as a double). When I try outputting mytypes.c
instead, it prints a blank square (indicating a character not being displayed correctly).
From what I understand about union, shouldn't it only hold a single value, of a single type? If that is true, wouldn't it be a float with the value 41.7341, and so calling it as double or char would throw an error?
Upvotes: 1
Views: 2739
Reputation: 2027
The purpose of a union is to have many types allocated to the same area of memory. So a union has memory and you can represent that memory based on any type you declared in the union.
If you explicitly cast a char to a double or vice versa would you get an error, no! A union is basically type-casting and less memory is used.
Upvotes: 0
Reputation: 2336
As noted in the other answers, all union members occupy the same memory space. You can always interpret the memory as a base type but the results may be unexpected.
I spiffed up the code to print out some details in hex. You can see that the memory will change as each successive value is assigned. The double doesn't get completely changed when the float is assigned thus the output value is close to the original. This is just a side effect of the type size and the hardware architecture.
As a side note, why is it such a pain to use cout to print hex characters.
#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
Upvotes: 1
Reputation: 2853
I'm going to make some assumptions about your types just to have concrete numbers for this explanation. I assume: char = 1 byte, int and float = 4 bytes, double = 8 bytes.
Now, they are saved in the same location in memory. Normally, if you declare an int and a float in a struct, the int will occupy the first 4 bytes, and the float will occupy the second 4 bytes. But the data saved is all 0's and 1's, and saving the same number as an int or float will have a different order of 0's and 1's because they are interpreted differently by the system. For example, int, float.
When you save to one of your union's fields, it takes the right sequence of 0's and 1's that correspond to what you've designated, and stores it there. If you save 15 to the int field, and read it as a float, you're going to get a completely different number, since you've forced it to read the number in a different order than it it was intended.
This all comes down to what @GManNickG said in their comment, it is undefined behavior. That sequence of 0's and 1's is still a number, just not the one you think it is. It is totally valid to read that in as a number and do computations with it. That is why something using a union usually has a second field, often an enum, defined separately from the union specifying which type is saved in it so that you know which to read when another part of the program uses it later.
Upvotes: 0