整数和浮点数在内存中的存储(C语言)

1.整数在内存中的存储
1.1 整数在二进制中的表示方法

整数在二进制中的表示方法有三种,即原码、反码、补码,三种表示的方法都有符号位数值位两部分,最高位表示符号位,其他都为数值位,符号位都是用‘0’表示‘正数’,用‘1’表示‘负数’,

原码:直接将数值按照正负数的形式翻译成二进制得到就是原码。

反码:将原码的符号位不变,其他位一次按位取反就得到反码。

补码:反码+1就得到补码。

✳:说明原码和补码之间的互相转换只需要取反+1

即原码取反+1=补码,补码取反+1=原码;

1.2 整型在内存中的存储表示

        对于整型来说,数据在内存中的存储其实放的都是补码。

原因:

①在计算机系统中,数值一律用补码来表示和存储

②使用补码可以将符号位和数值域统一处理

③同时,加法和减法也可以统一处理(CPU只有加法器),此外补码和原码相互转换运算过程相同,不需要额外的硬件电路。

1.3 代码实例

下面我们通过几段代码来感受一下整数在内存中的存储

1.3.1 代码一
#include<stdio.h>
int main()
{

  char a=-1;
  signed char b=-1;
  unsigned char c=-1;

  printf("a=%d\nb=%d\nc=%d\n", a, b, c);

  reurn 0;
}

运行代码就会发现上方代码的结果是: a=-1,b=-1,c=255(VS编译器结果)

为什么三个变量的打印结果不一样呢?原因就在于整数在内存中的存储方式

让我们来分析该代码:
首先我们定义了三个char类型的变量a,b,c,且都向里放入了整数-1

区别在于b是signed char(有符号字符型),c 是unsigned char(无符号字符型),

而a直接是char类型,根据c语言规定char类型是否带有正负号,由当前系统决定,即在不同的编译环境下,char的默认类型有可能是signed char也有可能是unsigned char,

根据上面我们所说的打印结果,a和b的打印结果相同可以看出:

在VS的环境下,char是默认为signed char即有符号字符型,即a和b可以等价分析

接下来我们使用printf函数打印三个变量,同时使用%d对其进行打印,

%d—以十进制的形式打印有符号的整数

接下来我们对三个变量放入内存中的存储过程进行分析

首先我们将-1以原码的形式进行表示(因为char类型占1字节=8 bit位即8位二进制数)

-1原码:10000001红色为符号位)(蓝色为数值位

-1补码(原码取反+1):11111111

因为%d是以十进制形式打印整型,所以这里要提到一个知识点——整形提升

简单来说就是当字符型短整型在进行某些算数运算和打印时,C语言会把这些操作数在使用之前转换为普通整数即int型

具体提升方式为:

①无符号的整形提升高位补0,

②有符号的整形提升高位补充符号位;

a 和 b 都是signed char(有符号字符型)类型,所以选择第②种提升,即高位补充符号位

int 型占4个字节即32个bit位,所以将其进行整形提升要补充24位,

整形提升后-1的补码为:

11111111  11111111  11111111 11111111(空格只是方便看,真实是连续的)

(橙色为补充的24位)(红色表示原补码符号位)

但是char类型只能存8位,所以这里会发生截断,即再从上方整形提升后的补码中取出最后8位存进char a(或者说signed char b)中

取出低位的8位:11111111,注意这里取出的是补码,想打印正确的值我们还应将其转换成原码

10000001——>>还原后的原码,可以看出还原后的原码对应的十进制数就是-1,所以a打印结果是-1

因为a和b可以等价分析,所以相同的分析过程,b的打印结果也为-1

那么c又是怎么回事呢?

其实过程是一样的,只不过c为无符号char,在整形提升时,稍有区别,

同样的-1补码(11111111)

整形提升后:

00000000  00000000  00000000 11111111

因为是无符号整形提升,所以提升时高位补0,而不再是符号位的1

同样因为char类型只能存储8位,所以进行截断,取该补码的最低8位即:11111111

此时因为视为无符号数即为正数,因为正数的反码、补码相同,所以其原码也为:11111111,换算为十进制即DEC为255

通过上述代码可以得到结论:

1、整形数据在内存中存储的都是补码,要得到正确的值必须转换为对应的原码

2、整形数据的存储和使用过程有时涉及整形提升、截断需要注意

同时可知道signed char的取值范围:-128~127,unsigned char取值范围:0~255

2. 大小端字节序和字节序判断

了解完整数在内存中的存储后,再来看一个细节

先看下列代码

int main()
{
	int a = 0x11223344;

	return 0;
}

我们定义一个十六进制数存放在int类型的变量a中,我们对代码进行调试,查看a在内存中的存储方式,可以看到a是按照字节为单位倒着存储

2.1 什么是大小端

为什么会出现上面的情况呢,原因是当超过一个字节在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,分别有大端字节序存储和小端字节序存储,具体概念如下:

大端(存储)模式:数据的低位字节内容保存在内存的高地址高位字节内容保存在内存的低地址

小端(存储)模式:数据的低位字节内容保存在内存的低地址高位字节内容保存在内存的高地址

内存的读写是从低地址到高地址进行的,以上述数据a为例0x11223344,按照不同的存储模式,从低地址到高地址应如下:

所以上述例子中为小端存储模式。

3. 浮点数在内存中的存储

我们先来看一段代码

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);

	*pFloat = 9.0;
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

打印的结果为

num和*pFloat明明在内存中指同一个数,为什么整数和浮点数的解读会有差别,原因在于浮点数和整数在内存中的存储方式不同

3.1 浮点数的存储

浮点数在计算机内部的表示方式是根据国际标准IEEE754来表示的,根据该标准任意一个二进制浮点数F可以表示为下面的形式:

表示符号位,当S=0时,F为正数,S=1时,F为负数

M表示有效数字,M大于等于1,小于2

表示指数位

举例:十进制数6.0表示为二进制数为110.0,相当于1.10x2^2,按照上面的格式可以看到,

此时,S=0,M=1.10,E=2

IEEE754 规定:

对于32位的浮点数,最高1位存储符号位S,接着的8位存储指数E,E转换为二进制数的形式表示,剩下的23位存储有效数字M

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

3.1.1 浮点数的存储过程

IEEE745对有效数字M和指数E,还有一些特殊规定

  前面提过M是大于等于1,小于2的数字,即M可以写成1.xxxx的形式,也就是M第一位数都为1,后面的xxx为小数,所以在保存M时可以丢弃默认的1,只保存小数部分。比如1.011时只保存011,等读取的时候再把1加上去。这样做可以节省一位有效数字。以32位为例子,M本来只有23位,将第一位舍去以后,等于可以保存24位有效数字。

  至于指数E,如果E为8位,它的取值范围为0~255=2^8,如果为11位,则取值范围为0~2047。但是我们知道科学计数法中的E是可以出现负数的,所以IEEE754规定,E在存入时它的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位这个中间数是1023。

3.2.1 浮点数的一些小点

1、有些浮点数是没办法在内存中精确保存的;

2、double类型的浮点数的精度比float类型的浮点数高

3、两个浮点数在比较大小时,直接使用==有时会出现问题,所以此时应采取精度措施,给定一个精度范围,当两个浮点数的差值在该范围内就认为两数相等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值