深度剖析数据在内存中的存储

目录

数据类型介绍 

按照其数据类型进行归类:

整形家族:

浮点数家族:

构造类型:

指针类型:

空类型(void):

对于整形数据要注意其存储数据的范围

整形数据在内存中的存储:

原码:

反码:

补码:

 大、小端介绍:

浮点型数据在内存中的存储:

浮点数转换为二进制表示:

浮点数的存储:

对于有效数字M:

对于指数E:


  • 数据类型介绍 

前边我们已经学习了一些基本的数据类型:int 、char、double……

按照其数据类型进行归类:

整形家族:

char:unsigned char、signed char

short:unsigned short、signed short

int:unsigned int、signed int

long(long int):unsigned long、signed long

long long:unsigned long long、signed long long

浮点数家族:

float、double、long double

构造类型:

数组类型

结构体类型(struct)

枚举类型(enum)

联合类型(union)

指针类型:

char*pc、int*pi、float*pf、void*pv……

空类型(void):

常应用于函数的返回类型、函数参数、指针类型。

对于整形数据要注意其存储数据的范围

首先要知道signed、unsigned的区别:

对于有符号数据(signed xxxx):二进制最高位仅表示数据的正负(0表示正、1表示负)

对于无符号数据(unsigned xxxx):二进制所有位都参与表示数据大小

以char类型为例:

char类型数据二进制表示:0000 0000、0000 0001、0000 0010、……、0111 1111、1000 0000、1000 0001、……、1111 1111

对于signed char:因为最高位仅表示正负,则从0000 0000到0111 1111表示正数(十进制0~127),从1000 0000到1111 1111表示负数,要先转化原码再求值(十进制-128~-1,理论上1000 0000应该表示-0,为了避免浪费,规定1000 0000表示-128),如果1111 1111再加1就会变成0000 0000(十进制0),即signed char表示的数据范围为-128~127;

对于unsigned char:因为最高位参与表示数据大小,则从0000 0000到0111 1111表示正数(十进制0~127),从1000 0000到1111 1111表示负数(十进制128~255),如果1111 1111再加1就会变成0000 0000(十进制0),即unsigned char表示的数据范围为0~255;

可以发现他们的都可以形成一个循环,画图表示出来;

  • 整形数据在内存中的存储:

首先要了解原码、反码、补码的概念:

原码:

直接将数字按正负数形式翻译成二进制。

反码:

原码符号位不变,其余各位安位取反。

补码:

反码+1。

对于正数:原码=反码=补码;

对于负数:补码=原码取反+1、原码=补码取反+1、原码=(补码-1)再取反。(符号位不能变,负数原码是通过求其对应正数原码,再把最高位变为1得到)

对于整型数据来说:数据在内存中以补码形式存储(可以将符号位和数值域统一处理,同时加减法也可以统一处理)。

以int型1(二进制补码00000000 00000000 00000000 00000001-->十六进制补码00000001)、-1(二进制补码11111111 11111111 11111111 11111111-->十六进制补码FF FF FF FF(ff ff ff ff))为例:

 可以发现-1在内存中确实是存储FF FF FF FF(ff ff ff ff),但是1在内存中的存储与事先推出的以    00 00 00 01形式存储有所不同,这是为什么呢?

这是因为大、小端的存在,导致数据存储时,会与预先推测不同。

  • 大、小端介绍:

大、小端(大、小端字节序)指的是数据在电脑上存储的字节顺序,顾名思义,其是以字节为单位存储(具体存储方式与电脑本身有关)。

大端存储:把数据的低字节内容存放到高地址高字节内容存放在低地址处;

小端存储:把数据的高字节内容存放到高地址低字节内容存放在低地址处。

知道了大小端的存在,那么前边1在存储时出现的问题就迎刃而解了,可以看出我用的电脑是小端存储。

因为-1十六进制补码全为f,所以无论是大端存储还是小端存储其显示出来都一样,但是1的十六进制补码为00 00 00 01,按小端存储应为01 00 00 00。

我们不妨再用20测试一下,20的二进制为00000000 00000000 00000000 00010100,十六进制为00 00 00 14,那么按照小端存储应为14 00 00 00

 可以发现与推测结果相同。

那么该如何判断自己的电脑是什么存储方式呢?

可通过编写一段代码来实现:

int b_s()
{
	int i = 1;
	return (*(char*)&i);//利用强制类型循转换,只截取第一个字节的内容
}
int main()
{
	int a = b_s();
	if (a == 1)
	{
		printf("小端\n");
	}
	else 
	{
		printf("大端\n");
	}
	return 0;
}

创建一个int型数据 i =1,然后通过强制类型转换把 &i 转换成char*型,再解引用访问 i 在内存中存储的第一个字节的数据,若该位为1,则为小端存储,否则为大端存储。

  • 浮点型数据在内存中的存储:

在了解浮点数的存储前,我们先来看一个例子:

#include<stdio.h>
int main()
{
	int n = 9;
	float* p = (float*)&n;
	printf("n=%d            ", n);
	printf("*p=%f\n", *p);

	*p = 9;
	printf("n=%d   ", n);
	printf("*p=%f\n", *p);
	return 0;
}

会输出什么呢?

运行结果:

 为什么会出现这样的结果呢?

首先我们知道浮点数肯定也是以二进制形式存储的,但他具体是如何存储的?是和整型数据的存储一样吗?

浮点数转换为二进制表示:

根据国际标准IEEE754,任意二进制浮点数N可以表示为以下形式:

  • N=(-1)^S*M*2^E
  • (-1)^S表示符号位,当S=0时,表示N为正数;当S=1时,表示N为负数
  • M表示有效数字,一般情况下1<=M<2
  • 2^E表示指数位

以9.0为例来说:9.0-->1001.0-->1.001*2^3-->(-1)^0*1.001*2^3   则S=0、M=1.001、E=3。

再以-5.5为例:首先要知道小数点后的计算权重为2^(-n),n表示第几位小数

-5.5-->-101.1-->-1.011*2^2-->(-1)^1*1.011*2^2  则  S=1、M=1.011、E=2。

可以发现其转换方式与十进制的科学计数法类似。

说完浮点数如何转化为二进制,再来说说他是如何存储的

浮点数的存储:

IEEE754规定:

对于32位浮点数,最高1位存储符号位S,接着8位存储指数E,剩下23位存储有效数字M

对于64位浮点数,最高1位存储符号位S,接着11位存储指数E,剩下52位存储有效数字M

在实际存储时,IEEE754对M和E还有一些特别的规定。

对于有效数字M:

前边说过1<=M<2,所以M可以写成1.xxxxxx的形式,在实际存储时,默认M第一位为1,因此直接舍去,只存储小数部分xxxxxxx,读取数据时取出小数部分再加上1即可。例如对于9.0(M=1.001),存储M时只存储001,后边空位补0,即001 0000 0000 0000 0000,这样做可以使23位(52位)的M可以多存储一位有效数字。

对于指数E:

存储E时:E为一个无符号数字。

E的取值范围0~255(0~2047),如果不加以处理,E只能为正数,无法表示负数,但是实际情况中存在E为负数的情况,因此就要在存储时对E进行处理。

IEEE754规定:在存储E时必须加上一个中间值127(1023)。例如9.0(E=3),存储时存储3+127=130,即1000 0010

读取E时:

  • E不全为0或不全为1:

取出指数E的值减去127(1023)得到真实值E,然后取出M再加上1,求出M*2^E后再看符号位确定符号即可得到最后结果。

  • E全为0:

此时浮点数E的真实值为1-127=-126(1-1023=-1022),有效数字不再加上1,直接还原为0.xxxxx

这样处理是为了表示±0(正负号由S决定),以及很接近0的数

  • E全为1:

此时如果有效数字M全为0,表示±∞(正负号由S决定),如果M不全为0,表示这个数不是一个数(NaN)。

现在来解释解释为啥会出现下边的情况

int型9在内存中存储为00000000 00000000 00000000 00001001,当把它强制转换为浮点型后,存储的存储的二进制形式不变,但是各位的意义发生了改变

 符合E取出规则的第二种,则转换后为(-1)^0*0.00000000000000000001001*2^(-126)=1.001*2^(-146),显然是一个及其接近0的数,而float只能精确到小数点后6位,所以打印出来为0.000000。

下边通过指针把n里边的内容修改为了一个浮点数9.0((-1)^0*1.001*2^3),那么存储的应该为     0 10000010 0010000 00000000 00000000,当把其按照整型还原时应为2^30+2^24+2^20=1091567616,所以以整型打印n时打印出1091567616。

因此平常练习时,要注意数据的类型。

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值