【c语言】详解 整数 与 浮点数 在内存中的存储

前言

   在计算机科学中,理解数据如何在内存中存储是至关重要的。这不仅有助于我们更深入地理解计算机的工作原理,还能够帮助我们编写出更高效、更健壮的程序。在所有的数据类型中,整数和浮点数无疑是最基础和最常用的两种。它们各自在内存中的存储方式既有相似之处,又有显著的不同。

    整数  作为计算机中最基础的数据类型之一,其存储方式直接反映了计算机内部的二进制表示。而浮点数,作为可以表示小数点的数据类型,其存储方式则相对复杂,涉及到了指数和尾数的概念。

    本文将深入探讨 整数  和 浮点数 在内存中的存储方式,从它们的定义开始,逐步揭示它们在计算机内部是如何被编码和存储的。我们将通过详细的解释和实例,帮助读者理解这两种数据类型在内存中的表示方式,以及它们之间的异同。

一:整数在内存中的存储

    对于整数在内存中存储的表示方法有三种:源码 反码 补码

       原码:我们将数字的 二进制表示 的最高位视为 符号位,其中 0 表示正数 ,1 表示负数,其余位表示数字的值。

       反码:正数的反码与其原码相同,负数的反码是对其原码除符号位外的 所有位 取反

       补码:正数的补码与其原码相同,负数的补码是在其反码的基础上 1 .

例如:

源码 反码 补码
+100000 .... 10100000 .... 10100000 .... 1010
-101000 .... 10101111 .... 01011111....0110

那么,为什么计算机要这么存储?

    在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统 一处理;
    同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程 是相同的,不需要额外的硬件电路。

举个例子 :

例如在原码下计算 1+(-2),得到的结果是 -3,这显然是不对的。

 为了解决这个问题,计算机就引入 反码 ,在 反码 运算后再 返回 源码,结果就是正确的。

    而 补码 的存在可以避免出现数值溢出的问题。在补码的表示方式中,正负数的范围是对称的,最高位的 1 代表的是 负数 最小值 ,而最高位的 0 代表的是 正数 最大值。这样,当进行运算时,如果发生溢出,即结果超出了正负数的表示范围,计算机可以自动忽略溢出部分,而不会导致错误的结果。

    而在计算机内的存储,也有所不同。

大小端的介绍

    对于 int 这种跨越多个字节的程序对象,我们有两个规则:

1、这个对象的地址是什么?

2、在内存中如何去排列这些字节?

第一个很好理解,地址就是存储对象的地方,我们通常用4字节、8字节大小来表示一个对象的地址

例如:

假设 int x 在32位的环境中运行 :x 的4个字节就会存储至<0x101 0x102 0x103 0x104>的位置上

第二个问题就是如何去排列这些字节。这就要引出大端、小端的知识。

    大端与小端也是有起源的:

    大小端之争的起源故事来自于Jonathan Swift的《格利佛游记》中的一个虚构情节。故事中有两个虚构的小人国,分别是Lilliput(利立浦特)和Blefuscu(不来夫斯库)。

    原本,两国人民吃鸡蛋的方式都是从鸡蛋较大的一端开始。然而,有一天,Lilliput国的皇帝祖父小时候在按这种方式吃鸡蛋时,不小心把手指弄破了。因此,他的父亲,也就是当时的皇帝,颁布了一项法令,规定全体臣民在吃鸡蛋时必须先打破鸡蛋较小的一端,违令者将受到重罚。

    这项法令在Lilliput国内引起了极大的不满和反抗,导致发生了多次叛乱。其中一个皇帝因此送了命,另一个丢了王位。而叛乱的原因之一,是Blefuscu国的国王大臣们煽动起来的。叛乱平息后,许多流亡者逃到了Blefuscu国寻求避难。

    这个故事实际上是对当时英国和法国之间持续冲突的讽刺。之后,Danny Cohen,一位网络协议的早期开创者,第一次用大端、小端来表示字节的存储顺序,随后就被广泛的接纳了。

那么,到底什么是大端与小端呢?

  大端(存储)模式:是指 数据的低位 保存在 内存的高地址 中,而 数据的高位 保存在内存的低地址 中;
小端(存储)模式:是指 数据的低位 保存在 内存的低地址 中,而 数据的高位 ,, 保存在内存的高地址 中。  

 依旧拿着上面的 int x来说:

假设 x 的十六进制值为0x 11 22 33 44 <32位>,存储字节为:<0x101 0x102 0x103 0x104>

大端法:

0x1010x1020x1030x104
.............11223344............

小端法:

低位......0x1010x1020x1030x104......高位
.............44332211............

解释:

内存中存储是,至左向右,地址依次增加。即 左为低地址,右为高地址

而 数据 ,与内存刚刚好相反。 左为高位,右为低位。(例如1001各个数字所对应的位数为:千 百 十 一,明显,千位数要高于百位数,依次类推)

巧记:“大端低高,小端高低”,即大端存储方式下,低位地址存放高位字节;小端存储方式下,低位地址存放低位字节。、

那么,为什么有大小端之分呢?

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

我在此处就不展开去深入讲解这部分知识,有兴趣的读者可以去《深入理解计算机系统》这本书,里面会涉及到在各个不同的编译器,环境下,存储方式之差以及移植所带来的困惑。

对于这部分内容,需要慢慢去吃透,对以后的内存如何运作有所帮助。

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

    浮点数,例如3.14  、1.12e10、 π 等等,我们都会说,使用浮点数会有失精度。为什么呢?

浮点数家族包括: float、double、long double 类型。
浮点数表示的范围:float.h中定义

例子:

int main()
{
 int n = 9;
 float *p = (float *)&n;
 printf("n的值为:%d\n",n);
 printf("*p的值为:%f\n",*p);
 *p = 9.0;
 printf("num的值为:%d\n",n);
 printf("*p的值为:%f\n",*p);
 return 0;
}

运行结果:

n的值为:9
*p的值为:0.000000
num的值为:1091567616
*p的值为:9.000000

接下来我将带着读者来了解,浮点数在内存中是如何存储的。

浮点数存储规则

根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式: 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。
IEEE 754规定:
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

 

而对于指数位E,具有特殊的含义:

指数位E有效数字M==0有效数字M!=0
0正负0次正常数字(失去精度的)
1-254正常数字正常数字
255正负无穷NaN(非常规数字)

参考文献:

《哈喽!算法》:Hello 算法

《深入理解计算机系统》

 

  • 35
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值