自 C89 以来,通过联合进行类型双关一直是合法的,因此那里没有未定义的行为,并且一些编译器明确保证它会起作用,例如,请参阅关于类型双关的 gcc 文档。他们需要这个,因为在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
一旦你修复了我看到的输出是这样的(现场查看):
13838435755002691587
这表明两个变量的变化都产生了影响。
您看到的结果是由于IEEE 754 二进制数的格式,如下所示:
这是显示数字的十六进制表示的几个示例之一:
3ff0 0000 0000 0002 16 ≈ 1.0000000000000004
c000 0000 0000 0000 16 = –2
因此,在您的情况下,分配一个负数var1.f
将设置至少一个高位。我们可以使用std::bitset在C++中轻松探索这一点,因为它们通过C++中的联合明确支持类型双关:gcc
#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;
}
我看到的结果是(现场观看):
13835058055282163712
1100000000000000000000000000000000000000000000000000000000000000
1100000000000000000000000000000000000000000000000000000000000000
-2.00000000000000133
1100000000000000000000000000000000000000000000000000000000000011
1100000000000000000000000000000000000000000000000000000000000011