【C语言学习】数据在内存中存储

一、整形在内存中的存储

整形数据具体如何在内存中存储,可以翻看前面关于操作符介绍:详解操作符,这里只简要介绍一下。

简单来说,整数在内存中是以二进制补码形式存储的,正整数的原码、反码、补码三码相同。针对有符号的整数,最高位是符号位,其余为数值位。

原码:直接将数值按照正负数的形式翻译成二进制;

反码:原码 + 1;

补码:反码 + 1;
原码取反+1得到补码,补码取反+1得到原码。

二、大小端字节序和字节序判断

把一个正整数10(0x0000000a)存入内存,按照我们的理解,a在内存中的存放顺序应该是00 00 00 0a,也就是也就是高位数据在前,低位数据在后,但是实际上内存是按照0a 00 00 00的顺序排列。

这是为什么呢?

这就涉及到大小端字节序的问题。

2.1 什么是大小端字节序

超过一个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存。
大端字节序:将一个数据的低位字节内容,存储到内存高地址处,高位字节的内容存储到内存低地址处。
小端字节序:将一个数据的低位字节内容,存储到内存低地址处,高位字节的内容存储到内存低地址处。

2.2 为什么会有大小端字节序

在计算机系统中,内存以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit位,但是在C语言中除了8bit的short型,32bit的char之外,还有16bit的long型(要看具体的编译器)。另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端储模式和小端存储模式。

一些处理器存储模式:X86结构的处理器是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

三、浮点数在内存中的存储

下面的代码运行结果:

c 复制代码
#include <stdio.h>
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;
}

解析:

按照我们的理解:将n的地址强制转换成float*类型赋给变量pFloat,虽然&n和pFloat指针类型不相同,但是它们指向的空间是一样的,通过解引用,按照%d、%f进行打印,结果就是9、9.000000(%f默认后面小数点6位)。

结果真的和我们预想的一样吗?

实际运行结果:

我们发现,代码运行的结果和我们设想的不完全不同。说明浮点数再内存中存储的方式或者读取的方式与整数是不一样的。

3.1 浮点数再内存中存储

IEEE协会规定浮点数存储方式:V = ( − 1 ) S (−1)^S (−1)S * M ∗ 2 E 2^E 2E
( − 1 ) S (−1)^S (−1)S表示符号位,当S=0,V为正数;当S=1,V为负数。
M表示有效数字,M是大于等于1,小于2的。
2 E 2^E 2E表示数位。

例如:5.0 = 101.0,5.0 = (-1)^0 * 1.01 * 2^2

9.5 = 1001.1,9.5 = (-1)^0 * 1.0011 * 2^3

但是有些时候,小数点后面全部转换成对应的二进制位比较困难,例如,5.2 ---> 101.001...,这也就是为什么浮点数没有办法在内存中精确保存。

另外,IEEE 754规定:
对于32位的浮点数:最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M。
对于64位的浮点数:最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M。

浮点数在存储的时候,S是直接放进去,但是E、M要进行处理。

有效数字M处理:

前面说过,1≤M<2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分。

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如说,保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。(简单来说,计算机在保存M的时候只保存小数点后面的位,不保存前面的1,但是读取的时候自己会加上1,这样存储精度会更高
数位E处理:
IEEE 754规定,E被看成是无符号整数。但是实际上E可能是负数(比如:0.5(十进制) ---> 0.1(二进制) ---> ( − 1 ) 0 (-1)^0 (−1)0 * 1.0 * 2(-1)。为了解决这个问题,需要给E的真实值加上一个中间值,32位加上127,64位加上1023。

举例:

1.2在内存中无法精确存储:

3.2 浮点数的读取

E不为全0,不为全1(一般情况):算出来减去127
E为全0:E真实值为-127,xxx*$2(-127) --- 无限接近于0的数字。这时候规定,浮点数的指数E等于1-127(-126)或者1-1023(-1022)即为真实值,有效数字M不再加上前面的1,而是还原为0.xxxxxx的⼩数。这样做是为了表示±0,以及接近于0的很小的数字。
E为全1:E的真实值为128,xxx*2^128 --- 非常大的数字。这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位S)。

我们把E全为零,E全为1两种情况称为非规范取值,整个float类型数据如下图所示:

有了前面的知识,再回到前面的代码为什么不是我们想的结果。

相关推荐
~无忧花开~2 小时前
JavaScript实现PDF本地预览技巧
开发语言·前端·javascript
靠沿3 小时前
Java数据结构初阶——LinkedList
java·开发语言·数据结构
4***99743 小时前
Kotlin序列处理
android·开发语言·kotlin
froginwe113 小时前
Scala 提取器(Extractor)
开发语言
t***D2643 小时前
Kotlin在服务端开发中的生态建设
android·开发语言·kotlin
qq_12498707533 小时前
基于springboot的建筑业数据管理系统的设计与实现(源码+论文+部署+安装)
java·spring boot·后端·毕业设计
一 乐3 小时前
宠物管理|宠物共享|基于Java+vue的宠物共享管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·springboot·宠物
a crazy day3 小时前
Spring相关知识点【详细版】
java·spring·rpc
Elias不吃糖3 小时前
LeetCode每日一练(209, 167)
数据结构·c++·算法·leetcode