猜测一下上图打印出来的结果是什么,我们可以看到指针变量pi接受了i的地址,所以对指针变量进行解引用后就是变量i,那么打印时i和*pi的结果应该是相同的。但结果是否跟我们所想的相同呢?
看图可以知道,打印出来的结果和我们猜想的不同,那么为什么不相同呢?我们仔细观察,pi是一个float*类型的指针变量,我们接收i的地址时因为i是int类型的变量,所以我们先将它的地址转换成float*类型再用pi进行接收。然后就造成了如图的打印结果。由此我们可以发现程序对整型和浮点数从内存中的提取方式应该是不同的,才造成了打印的这种结果。
那么我们就要学习一下浮点数是如何在内存中存储的。
首先我们知道,所有数据都可以用二进制的形式表达出来的,而根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,又单独规定了浮点数的表达方式。如下图:
所以一个浮点数就可以用上面的方法表示出来,如浮点数5.5的二进制表达式为101.1,用科学计数法可以表达为1.011*2^2,而用IEEE又可以表达为(-1)^0*1.011*2^2,其中S=0,M=1.011,E=2。而存贮用这种方式表达的浮点数时又有特别的方法。
IEEE 754规定: 对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M。对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M。如图所示。
而关于M和E的存储也有特别的规定。
1.关于M的存储:
我们提到过,M是大于1,小于2的数字,所以我们在存储M时舍弃了小数点前的1,只存储小数点后的位数,例如存5.5时,M区域存储的就是01100000000000000000000。
2.关于E的存储:
当存储数字0.25时,先转换成二进制0.01,再转换成浮点数的表达(-1)^0*1.0*2^-2,此时E是等于负数的,说明E可能为负数也可能为正数,所以IEEE 754规定,存⼊内存时E的真实值必须再加上 ⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。⽐如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
学习了浮点数的存储,接下来就要学习浮点数从内存中取出来的方法。
1.E中有0也有1:
这种时候在取出E时要减去我们存储时加的中间值,而在拿出M时 在前面自动补1.
2.E中全为0时:
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,⽽是还 原为0.xxxxxx的⼩数。这样做是为了表示±0,以及接近于0的很⼩的数字。
3.E中全为1时:
这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s)。
学习浮点数的存储和取出后,我们就可以知道为什么最开始打印的结果是那样了。
首先i是int类型,而指针pi是float类型的,所以pi在接收i的地址时要先强制转换成int*的类型,9的二进制是1001,存入内存时,首先是以整型的方式打印的,所以是九。
第二个,i处内存中存的是而pi是float类型的指针,打印时就会认为pi所指向的空间中存的是浮点数,所以打印时会把他以浮点数的方式取出来,就变成(-1)^0*0.0000000000000000001001*2^(1-127),因为浮点数是默认保留六位小数的,所以打印出来后就是0.000000.
第三个,我们*pi=9.0。9.0是一个浮点数,所以存储时要用浮点数的存储方法。
所以在用%d打印时就会认为是一个整型,直接将0 1000 0010 00100000000000000000000转换成整数。而第四个就会以浮点数的方式取出所以打印的结果是正确的。
以上就是我对C语言中浮点数存储和取出方式的解答,如有错误请指出。