联合存储多个值

Union storing multiple values

代码如下:

#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(声明为双精度值)。当我尝试输出 mytypes.c 时,它会打印一个空白方块(表示字符未正确显示)。

根据我对 union 的理解,它不应该只包含单一类型的单一值吗?如果那是真的,那么它不是一个值为 41.7341 的浮点数,因此将其称为 double 或 char 会引发错误吗?

联合的目的是将许多类型分配到同一内存区域。所以一个联合体有内存,你可以根据你在联合体中声明的任何类型来表示那个内存。

如果您将 char 显式转换为 double 或将 char 转换为 double 会出现错误,不会!联合基本上是类型转换,使用的内存更少。

我将对您的类型做出一些假设,以便为该解释提供具体数字。我假设:char = 1 个字节,int 和 float = 4 个字节,double = 8 个字节。

现在,它们保存在内存中的相同位置。通常情况下,如果在结构中声明一个int和一个float,int会占据前4个字节,float会占据后4个字节。但是保存的数据都是0和1,把同一个数保存为int或float,0和1的顺序会不同,因为它们被系统解释不同。例如,int, float.

当您保存到联合字段之一时,它会根据您指定的内容采用正确的 0 和 1 序列,并将其存储在那里。如果将 15 保存到 int 字段,并将其作为 float 读取,您将得到一个完全不同的数字,因为您已强制它以与预期不同的顺序读取数字。

这一切都归结为@GManNickG 在他们的评论中所说的,这是未定义的行为。 0 和 1 的序列仍然是一个数字,只是不是你认为的那样。将其作为数字读入并用它进行计算是完全有效的。这就是为什么使用 union 的东西通常有第二个字段,通常是一个枚举,与 union 分开定义,指定保存在其中的类型,以便您知道程序的另一部分稍后使用它时要读取哪个。

如其他答案所述,所有联合成员都占用相同的内存space。您始终可以将内存解释为基本类型,但结果可能出乎意料。

我整理了代码以十六进制打印出一些细节。您可以看到内存会随着每个连续值的分配而改变。当分配浮点数时,double 不会完全改变,因此输出值接近原始值。这只是类型大小和硬件架构的副作用。

附带说明一下,为什么使用 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