意外的联盟行为

时间:2014-07-18 22:05:21

标签: c floating-point double output unions

以下代码每次输出不同的数字。
apples.num打印2这是正确的,apples.weight每次打印不同的数字,它甚至打印出“nan”,我不知道为什么会发生这种情况..
真正奇怪的是双(apples.volume)打印出2.0 ..

有人可以向我解释一下吗?

#include <stdio.h>

typedef union {
    short num;
    float weight;
    double volume;
} Count;

int main(int argc, char const *argv[])
{
    Count apples;
    apples.num = 2;

    printf("Num: %d\nWeight: %f\nVolume: %f\n", apples.num, apples.weight, apples.volume);
    return 0;
}

3 个答案:

答案 0 :(得分:3)

在我看来,你不太了解工会是什么。联合的成员是重叠值(换句话说,Count联合的三个成员共享相同的空间)。

假设,仅为了演示,short是16位(2字节),float是32位(4字节),double是64位( 8字节),然后联合大小为8字节。在little-endian格式中,num成员引用前2个字节,weight成员引用前4个字节(包括num的2个字节),卷成员引用到完整的8个字节(包括num的2个字节和weight的4个字节。)

最初,你的联盟包含垃圾,即一些未知的位模式,让它像这样显示(十六进制):

GG GG GG GG GG GG GG GG   // GG stands for garbage, i.e. unknown bit patterns

如果将num设置为2,则前两个字节为0x02 0x00,但其他字节仍为垃圾:

02 00 GG GG GG GG GG GG

如果您阅读weight,则只是读取前四个字节,解释为float,因此float包含字节

02 00 GG GG

由于浮点值具有与short等整数类型完全不同的格式,因此您无法预测这些字节(即特定位模式)所代表的内容。它们不代表浮点值2.0f,这是您可能想要的。实际上,&#34;更重要的&#34; float的一部分存储在高位字节中,即存储在&#34;垃圾&#34; weight的一部分,因此它几乎可以是任何内容,包括NaN+infinity-infinity等。

同样,如果您阅读volume,则您有double由字节组成

02 00 GG GG GG GG GG GG

并且这也不一定代表2.0(尽管偶然地,它可能非常接近,如果巧合的是,正确的位设置在正确的位置,并且如果低位在您显示这样的值时被舍去掉)。

联盟无意从int转换为floatdouble。它们仅仅意味着能够将不同类型的值存储到同一类型中,并且在设置时从另一个成员读取只是意味着您重新解释一些位在联盟中存在完全不同的东西。您不是转换

那你怎么转换?它非常简单,不需要联合:

short num = 2;
float weight = num; // the compiler inserts code that performs a conversion to float
double volume = num; // the compiler inserts code that performs a conversion to double

答案 1 :(得分:2)

如果通过“错误”成员(即通过其分配的成员以外的成员)访问联合,则结果将取决于该类型的特定位模式的语义。如果指定的类型具有他访问类型的较小位宽,则其中一些位将是未定义的。

答案 2 :(得分:1)

您正在访问未初始化的数据。它将提供未定义的行为(即:在这种情况下为未知值)。 You also likely mean to use a struct instead of a union.

#include <stdio.h>

typedef union {
    short num;
    float weight;
    double volume;
} Count;

int main(int argc, char const *argv[])
{
    Count apples = { 0 };
    apples.num = 2;

    printf("Num: %d\nWeight: %f\nVolume: %f\n", apples.num, apples.weight, apples.volume);
    return 0;
}

通过将其归零或将最大成员设置为值来初始化并集。即使您设置了最大的成员,其他值也可能没有意义。 This is commonly used for creating a byte/word/nibble/long-word data type and making the individual bits accessible.