我正在根据下面的输出寻找一些解释,我明白有一种类型转换..但是在为double \ int数据类型赋值时会发生什么?哪些位填充?指针指向哪些位?
#include <stdio.h>
int main() {
double d = 2;
double *pd = &d;
int *pi = (int *) &d;
printf("pi = %p, pd = %p, *pd = %f, *pi = %f\n", pi, pd, *pd, (double) *pi);
return 0;
}
输出:
pi = 0x7fff5504ba70, pd = 0x7fff5504ba70, *pd = 2.000000, *pi = 0.000000
答案 0 :(得分:1)
double *pd = &d;
int *pi = (int *) &d;
无法保证_Alignof(double)>=_Alignof(int)
或d
恰好恰好对齐。如果不是,则表示您有未定义的行为。
printf("pi = %p, pd = %p, *pd = %f, *pi = %f\n", pi, pd, *pd, (double) *pi);
现在printf
- 召唤是一个坏人:
void*
,但实际上是int*
。 UB。void*
,但实际上是double*
。 UB再次。*pi
违反了别名。 UB又来了。在实践中,现代系统上的事情几乎不可怕。只有最后一个可能在那里有危险,但两者都没有定义。
您所看到的内容可以很容易地解释,假设上面没有任何UB做任何事情&#34;令人惊讶&#34; (这可能是令人惊讶的),初始化程序实际上是2而不是4
还假设int
为32位且double
为IEEE double-precision floating-point-format,保存为little-endian:
bits
63 Sign-bit 0 (positive)
62-52 Biased Exponent (1023+1 == 1024 == 100_0000_0000<sub>2</sub>
51-0 Mantissa (All zeroes, the implicit 1 before the decimal-point is not saved, ever)
因此,低阶32位为零,通过将它们解释为int
来获取。
答案 1 :(得分:0)
pi = 0x7fff5504ba70:这是d
的内容pd = 0x7fff5504ba70:无论指针类型如何,它都是d的地址
* pd = 2.000000:应该是4.000000。这是一个错字吗? (编辑 - 你纠正了你的问题所以现在这是正确的)
* pi = 0.000000:它是0x7fff5504ba70处的位的值被解释为整数(因为pi是指向int的指针),然后转换为double(因为你已经转换为double)。在我的机器上,4为双倍是0x4010000000000000。所以前4个字节都是0(因为在我的机器上,cpu是little-endian,double是8个字节,int是4个字节)。
发生的事情是pi指向与pd相同的地址,但是当你取消引用pi时只考虑4个第一个字节,这4个字节都是0。
答案 2 :(得分:0)
你骗了编译器。
您将pi
指向d
的地址。这不会改变pi
指向仍具有double
的类型和位表示的对象的事实。
当您访问(double)*pi
时,您的程序会尝试解释双精度的位,就好像它是int
一样。结果(通常)是一个无意义的值,然后转换为double
。
如果指针未正确对齐以访问int
,它甚至可能会导致程序崩溃。
答案 3 :(得分:0)
只要您尝试将一种类型的内部表示(此处为double)解释为另一种类型的内部表示(此处为int),您将获得未定义的行为:任何版本的C规范都不会涵盖此内容。
换句话说,它依赖于编译器和体系结构。许多现代架构使用IEEE 754作为浮点数,即尾数(或sigificand)部分和指数部分,全部在基数2中。
Name Common name Base Significand E min E max Decimal Decimal
digits digits E max
binary32 Single precision 2 24 −126 +127 7.22 38.23
binary64 Double precision 2 53 −1022 +1023 15.95 307.95
但是没有什么能够迫使某个特定的架构使用这样的表示,所以你需要将代码引导到UB,除非你把它记录下来以适应一个特定的系统。
参考文献: