#include <stdio.h>
#include <stdlib.h>
typedef union
{
double f;
unsigned long long u;
int long long i;
} r;
int main()
{
r var1, var2;
var1.f = -3.5;
var2.u = 3;
var1.u = var1.u + var2.u;
printf("%u", var1.u);
return 0;
}
为什么这只返回var1的值而不是总和?
如果var1
和var2
添加了相同的指定数据类型,则它可以正常工作。
我以为 union 认为这是一个非问题?
答案 0 :(得分:3)
从联盟的其他成员读取而不是您上次指定的成员会导致未指定的值。它并非无效,但标准没有规定如何解决类型惩罚,结果可能是陷阱重新反应..参见:
Is type-punning through a union unspecified in C99, and has it become specified in C11?
工会的目的不是允许打字。它允许您通过在两个不同的变量中重复使用相同的内存来节省空间,当您知道它们不会同时需要它们时。有用这个例子,请参阅:
How can a mixed data type (int, float, char, etc) be stored in an array?
(这恰好是我投票最高的答案)。
答案 1 :(得分:2)
通过联合的类型惩罚已经legal since C89,因此没有未定义的行为,并且有几个编译器明确保证它会起作用,例如参见gcc documentation on type-punning。他们需要这个,因为在 C ++ 中它并不是那么明确。
但是这条线肯定有未定义的行为:
printf("%u", var1.u);
var1.u
的类型是 unsigned long long ,因此正确的格式说明符应该是%llu
,而clang
正好抱怨如下:
warning: format specifies type 'unsigned int' but the argument has type 'unsigned long long' [-Wformat]
printf("%u", var1.u);
~~ ^~~~~~
%llu
一旦你修复了我看到的输出( see it live ):
13838435755002691587
表明两个变量的变化都有效。
您看到的结果归因于IEEE 754 binary number的格式,如下所示:
这是显示数字的十六进制表示的几个示例之一:
3ff0 0000 0000 0002 16 ≈10000000000000004
c000 0000 0000 0000 16 = -2
因此,在您的情况下,为var1.f
分配一个负数将设置至少一个高位。我们可以使用std::bitset和gcc
轻松地在 C ++ 中进行探索,因为它们明确支持 C ++ 中的联合的类型惩罚:
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
typedef union
{
double f;
unsigned long long u;
int long long i;
} r;
int main()
{
r var1, var2;
var1.f = -2 ; // High bits will be effected so we expect a large number for u
// Used -2 since we know what the bits should look like from the
// example in Wikipedia
std::cout << var1.u << std::endl ;
std::bitset<sizeof(double)*8> b1( var1.u ) ;
std::bitset<sizeof(double)*8> b2( 13835058055282163712ull ) ;
std::cout << b1 << std::endl ;
std::cout << b2 << std::endl ;
var2.u = 3;
var1.u = var1.u + var2.u; // Low bits will be effected so we expect a fraction
// to appear in f
std::cout << std::fixed << std::setprecision(17) << var1.f << std::endl ;
std::bitset<sizeof(double)*8> b3( var1.u ) ;
std::bitset<sizeof(double)*8> b4( 13835058055282163715ull ) ;
std::cout << b3 << std::endl ;
std::cout << b4 << std::endl ;
return 0;
}
我看到的结果是( see it live ):
13835058055282163712
1100000000000000000000000000000000000000000000000000000000000000
1100000000000000000000000000000000000000000000000000000000000000
-2.00000000000000133
1100000000000000000000000000000000000000000000000000000000000011
1100000000000000000000000000000000000000000000000000000000000011
答案 2 :(得分:1)
嗯。
在联合字段中驻留在同一物理空间中。也就是说,联合的大小大致是其最大领域的大小。您指定union的float字段,然后尝试用作整数值。这会导致未定义的行为,更确切地说,此行为取决于目标平台上整数和浮点数的表示。
坦率地说,可能使用此技巧进行某种转换(例如,如果您需要将一对机器字“转换”为单个双字),但每次使用此技术时应该清楚地了解目标CPU架构的血腥细节。
我的一个朋友曾经在SPARC计算机上遇到虚假的段错误,因为他试图使用类似的技术访问非对齐数据:)
示例:
alex@galene ~/tmp $ cat test_union.c #include <stdio.h> typedef union { float f; unsigned long long ull; } csome; int main(void) { csome cs; csome cs2; printf("&f = %p, &ull = %p\n", &cs.f, &cs.ull); cs.f = 3.5; cs2.ull = 3; cs2.ull = cs.ull + cs2.ull; printf("cs2.ull = %Ld\n", cs2.ull); return 0; } alex@galene ~/tmp $ cc -Wall -o test_union test_union.c alex@galene ~/tmp $ ./test_union &f = 0xbfee4840, &ull = 0xbfee4840 cs2.ull = 1080033283
您可能会看到cs2.ull
的值是“随机”