C语言学习(九)整数在内存中是如何存储的?数值溢出的本质是什么?从源头了解奇怪的整数输出问题

本文详细介绍了计算机中整数如何以补码形式存储,探讨了数值溢出的本质,以及加减法运算在硬件层面的简化。通过原码、反码、补码的概念,解释了负数在内存中的表示方式,并通过实例展示了溢出导致的计算错误。同时,文章讨论了不同整型类型的取值范围,以及无符号数和有符号数溢出后的不同表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C语言学习(九)整数在内存中是如何存储的?数值溢出的本质是什么?从源头了解奇怪的整数输出问题

关于计算机中的加法和减法,设计计算机时是如何简化硬件电路的?

加法和减法是计算机中最基本的运算,计算机时时刻刻都离不开他们。所以他们由硬件直接支持。为了提高加减法的运算效率,硬件电路要设计的尽量简单。

对于有符号数,人脑很容易区别。但对于计算机来说,内存要区分符号位和数值位,这就要设计专门的电路,增加了硬件的复杂性,增加了计算时间。如果能把符号位和数值位等同起来,让他们一起参与运算,不特意区分他,这样硬件电路就简单多了。

加法和减法可以合并为一种运算,就是加法运算。因为减去一个数相当于加上这个数的相反数,例如,5-3等价于5+(-3),10-(-9)等价于10+9。

相反数指的是数值相同,符号不同的两个数。如,-3和3是一对相反数,-9和9也是一对相反数

如果能实现上面的目标,那么只要设计成一种简单的、不用区分符号位和数值位的加法电路,就能同时实现加法和减法运算,并且高效。实际上这已经实现了,真正的计算机电路就是这么简单。

然而,简化电路是有代价的,这个代价就是有符号数载存储和读取时都要经过一定的转换。那么,这个转换过程是怎样的呢?下面我们来说说。

原码、反码、补码

我们先记住几个概念。

原码

将一个整数转换成二进制形式,这个二进制就称为这个整数的原码。如short a = 6;,a的原码就是0000 0000 0000 0110,更改a的值a = -18;,此时a的原码变成了1000 0000 0001 0010。原码就是一个整数的二进制表示。

反码

正数和负数的反码不一样。

对于正数,他的反码就是他的原码(原码和反码相同);而负数的反码是将除了符号位以外的所有位取反,也就是0变成1。如short a = 6;,a的原码和反码都是0000 0000 0000 0110;如果更改a的值a = -18;,那么此时a的反码是1111 1111 1110 1101

补码

正数和负数的补码也不一样。

对于正数,他的补码就是他的原码(原码、反码、补码都相同);负数的补码是其反码加1。如short a = 6;,a的原码、反码、补码都是0000 0000 0000 0110;更改a的值为a = -18;,此时a的补码是1111 1111 1110 1110

总结

可以认为,补码是在反码的基础上打了一个补丁,进行了一个修正,所以叫“补码”。

原码、反码、补码只对负数有实际意义,对于正数,他们都一样。

至于,为什么需要这几个概念,我们将在后文解释。

先让我们总结一下6和-18从原码到补码的转换过程:
在这里插入图片描述

另外,有一点很重要。在计算机内存中,整数一律采用补码的形式来存储。这意味着,当读取整数时还要采用逆向的转换,也就是将补码转换成原码。将补码转成原码就是先减1,再将数值位去反。

采用补码存储是如何简化硬件电路的?

我们现在假设6和18都是short类型的(在内存中占有2Byte,也就是16B),先计算6-18的结果,根据我们上面说的,他等价于6+(-18)

如果我们用原码直接计算,那么运算过程为:

6 - 18 = 6 + (-18)

= [0000 0000 0000 0110] + [1000 0000 0001 0010]

= [1000 0000 0001 1000]

= -24

我们可以看出,用原码表示整数,让符号位也参与运算,对于类似上面的减法来说,结果是不正确的。

经过人们的探索,后来设计出了反码。下面演示了反码的运算过程:

6 - 18 = 6 + (-18)

= [0000 0000 0000 0110] + [1111 1111 1110 1101]

= [1111 1111 1111 0011]

= [1000 0000 0000 1100]

= -12

这样计算结果就正确了。

然而这样还不算结束,我们现在将减数和被减数换一下位置,计算一下18-6的结果:

18 - 6 = 18 + (-6)

= [0000 0000 0001 0010] + [1111 1111 1111 1001]

= [1 0000 0000 0000 1011]

= [0000 0000 0000 1011]

= 11

按照反码的计算结果是11,而真实结果应该是12,他们相差了1。

注意,蓝色的1是加法运算过程中的进位,他溢出了,内存容纳不了,所以直接截掉

6-18的结果正确,而18-6的结果就不正确,相差1.按照反码来算,是不是小数减大数正确,大数减小数就不对,始终相差1呢?

是这样的,不妨我们再看看其他的例子:

5 - 13 的运算过程为:

5 - 13 = 5 + (-13)

= [0000 0000 0000 0101] + [1000 0000 0000 1101]

= [0000 0000 0000 0101] + [1111 1111 1111 0010]

= [1111 1111 1111 0111]

= [1000 0000 0000 1000]

= -8

13 - 5 的运算过程为:

13 - 5 = 13 + (-5)

= [0000 0000 0000 1101] + [1000 0000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JayerZhou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值