在编程里,数据是存放在变量里的,而变量又有许多类型,但我们只知道什么类型有几个字节,比如int是整形数据,存放整数,占4个字节,却不知道数据在计算机里是怎么存储的。在C语言里整形和字符的存储是类似的,而浮点型(有小数)数据的存储却有很大不同。
目录
一.整数和字符型数据的存储
整数存放的就是二进制的补码,字符则是按照字符对应的ASCII码对应的数字进行的存储,也是整数。
有关二进制的原反补码和十进制和二进制的互相转换有不懂的可以参考下面这篇文章:
#include<stdio.h>
int main()//创建一个int型和char型变量查看他们在内存中的存储方式
{
int a = 1;
char ch = 'A';
return 0;
}
第一张图是变量a在内存中的存储方式,显示的是16进制的数字,值是00000001(int是占4字节也就是32bit位,16进制一个数字代表4位),是10进制的1。
第二张图是变量ch在内存中的存储方式,因为字符A的ASCII码是65,而这里的值是16进制的41(char是占1字节也就是8bit位,16进制一个数字代表4位),换算成10进制就是65。
当然这里数据读起来有些奇怪,我们一般是从左向右读一个数的,这里是从右向左(在单个字节里是从左向右读的,图里每两个相邻的数字就是一个字节)。
这里就涉及到大端小端的知识了,是数据在电脑上存储的二进制字节序。
二.大端小端(数据存储的二进制字节序)
大端模式,是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中。小端模式,是指数据的低位保存在内存的低地址中,而数据的高位 保存在内存的高地址中。
下图是16进制11223344数字在小端机器上内存中的存储,可见低位是存放在低地址,高位是存放在高地址,大端则相反。
下面一段代码可以判断你当前电脑是大端还是小端。
#include<stdio.h>
int main()
{
int a = 1;
if (*(char*)&a == 1)//取出a的地址转换成char*解引用第一个字节判断是0还是1
printf("小端\n");
else
printf("大端\n");
return 0;
}
三.浮点数的存储
浮点数在计算机中的存储遵循了IEEE754标准,一个浮点数可以表示为以下标准。
知道了浮点数是如何计算的,那么float(4字节)和double(8字节)有什么区别呢。
下面是float和doubleS,E,M分配的bit位数。
有了上面float和double的存储方式,我们可以大胆推测一下5.0在内存中是如何存储的。
我们创建了一个float类型的变量a,值是5,但是在内存可以看到16进制是0x40a00000,转换成2进制是计算机上面的数字,可以看到第一个数字符号位S是0,M是前面两位存的是01,和之前的计算一样,但是红线画的是E不是10(十进制的2)而是10000001。
这是因为,E其实是一个无符号整数,但是按照科学技术法算的话,E是可以为负数的,所以IEEE754规定,E必须加上一个中间数,每次计算的时候会减去中间数,然后才进行正常运算。
float类型的E是8bit,取值范围是0~255,所以每次计算时会减去中间值127。
double类型的E是11bit,取值范围是0~2047,所以每次计算时会减去中间值1023。
回看上图,E是10000001,换算成10进制是129,又因为是float类型,中间值是127,129减去中间值127刚好等于2,也就是二进制的10,十分符合我们先前的推测。所以浮点数在写入内存时指数E应该加上中间值,而取出来运算时应减去中间值。
当E全为0时,有效数字M就不用加上1了,而是0.xxxxxxxx,表示这是一个非常小的数字,十分接近0。
当E全为1时,说明这个数字是无穷的,S=1或0则说明这是一个无穷大或者无穷小的数字。
5.5这个数的S=0,E=129-127(中间值),M=011,最后科学计数法表示出来就是:
(-1)^0 * 2^2 * (1.011)=101.1=5.5。
但是按照IEEE754标准并不能精确的表示所有小数,比如5.7。
分析计算器里的数据,S=0,E=129-127(中间值),M=011 0110 0110 0110 0110 0110,科学计数法表示出来的就是:
(-1)^0 * 2^2 * (011 0110 0110 0110 0110 0110)=101.1 0110 0110 0110 0110 0110=5.69999981。只能无限近似5.7。
以上就是数据在内存中的存储,long long 和 long 和 int char类型存储方式是一样的,double在输出范围和精度上高于float。