赞
踩
C语言中无论是字符型还是整数型亦或是浮点型都已二进制存储在地址空间中。其类型决定在内存地址开辟多大的空间。此篇文章主要谈一下如何存储在内存空间中。
谈整型存储方式不得不说: 原码——反码——补码。这里简单提一下:
原码:直接将二进制按照正负数的形式翻译成二进制就可以。
反码:将原码的符号位不变,其他位依次按位取反就可以得到了。
补码:反码+1就得到补码。
对于整形来说:数据存放内存中其实存放的是补码。那为什么呢?
1>在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;(怎么理解呢?)
以char类型为例:-1+3=?
-1 补码:1111 1111
3 补码: 0000 0011
补码相加结果为:0 0000 0010 (这是补码转换原码在转换为十进制结果:2)
最高位0溢出(char 一个字节 8位 故9位必溢出)
补码转换原码在转换为十进制结果:2 很显然正确。
但如果使用原码呢?结果 1000 0100 错 反码呢? 0 0000 0001 也错
2>加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。(这里硬件电路问题了解就好)
知道整数型以补码存在地址中,例如
那该怎样读取出来呢?0900 0000? 还是 0000 00 09? 0009 0000?
必须要有一个规则,这个规则就是大端模式和小端模式
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地
址中。
举个例子理解 假如int a = 0x 11 22 33 44
如果内存中低地址存放 44 就是小端模式
同理内存中低地址存放 11 就是大端模式
浮点数并不像整形一样以简单的二进制存储在内存中。其存储规则是根据IEEE754为标准。
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S * M * 2^E
(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位。
举例来说:
十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。
那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。那么,s=1,M=1.01,E=2。
对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。如图:
前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存小数点后面部分。
对E(0-255)来说 2^E E怎么表示负指数呢? 这里采用了将指数加上127在存入E的八位地址中,这样E里面存入实际对应的指数取值为 -127~128。
double类型同理:
下面代码输出结果是什么呢??
int main()
{
int n = 9;
float *pd = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pd的值为:%f\n",*pd);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pd的值为:%f\n",*pd);
return 0;
}
9 9.000000 9 9.000000 真的对吗?
实际上结果是:
9 0.000000 1091567616 9.000000 为什么呢?
第一个输出9 很简单
第二个 float *pd = (float *)&n;
将int n ;中数据改变了数据类型,将其变为浮点型,(重点来了)(1)这个地址中存储数据并未改变; (2)pd 与 n 地址是一样的。但是pd 改变了类型故采取了浮点数读取,其指数为E全为0 也就是 2^-127 故输出为0.000000
第三个 浮点数存入 (S E M方式存入)但读取以整数反码读取 方才出现了输出:1091567616
第四个很简单
浮点数(float)读取 int 存储空间值发生了什么? 这个问题答案很显而易见了: 本质上还是以自己读取方式读取内存中的数据,只是与存入方式不同罢了(之前浮点数以浮点数SEM存入读出,现在以int 反码存入 以SEM读出罢了)。
可能这时候有人会问了强制类型转换为什么 int 强制转换 float 输出为 9.0 啊(个人理解,也不太懂,欢迎解惑)
int a = 9;
float b = (float)a; // 创造了一个新的数据
printf("%f\n", b);
这是为什么呢?还记得上边强调的吗 —— *pd 与 n 地址是一样的。相当于他们是在同一个“柜子”中,用不同的方式取出数据,结果不同。
强制类型转换,他没有改变 a 地址中的值,但其实在强制类型转换的操作中计算机自动开辟出了一块新的空间,读取数据并未a 地址中读取,而是读取新开辟的地址中的值。
学到上述所有知识后 我脑海中有一个新的问题。如下:为什么第三个是0.000000呢
int c = 9;
float* d = (float*)&c;
*d = 9.0;
printf("%d\n", c); // 1091567616 上面讲过
printf("%f\n", *d); // 9.000000 上面讲过
printf("%f\n", c); // 0.000000 为什么是0
我也请教过一些人,都没有得到明确的解答。
这里请各位读者明白 printf("%f\n", c);
这种代码是错的,不要写,千万不要写。一个 int 为什么用 %f 输出 ??? 这本身就是错的,但抱着求知态度,查阅很多网上解释,了解到要解释这个问题需要了解以下知识:
- 不同编译器和操作系统的ABI以及寄存器的使用规则
- 浮点数在内存的表示方式
- LIBC里的printf的具体实现方式
其实没必要知道为什么,只需要知道这本身就是错的就足够了。但如果有人想深入了解这个问题,我找到一篇比较靠谱的解释,请移步知乎 北极作者 我觉得解释还是比较靠谱的,希望对各位有启发,欢迎留言一起讨论。
最后:第一次写文章, 目的:一是巩固学习,二是分享学习中见解。有错误,望批评指正,共同进步。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。