C语言中的数据存储

Last updated on December 13, 2023 pm

减法问题

以下讨论如无特殊说明,均假定发生在数据宽度为16的情景下,即0x00~0xFF范围。

在计算机只能支持加法运算的前提下,通过改变存储数据的方式,使其可以支持减法运算。

让我们来计算 B - A = ? 的问题。

正负数与符号位

假定需要在0x00~0xFF范围内定义正负数,令0x00~0xFF呈环状排列,即:

0->1->2->3->…->0xFE->0xFF->0->1->…

则做如下定义:从0至0x7F,表示十进制整数0 ~ 127; 从0x80至0xFF,表示十进制整数-128 ~ -1

该分法直接将0xFF范围内整数切分为两份,转换为二进制数可发现,最高位为0的数皆为正数,最高位为1的数则为负数。

16进制 二进制 最高位 符号
0x5D 0101 1101 0 正数
0x7F 0111 0000 0 正数
0x80 1000 0000 1 负数
0xFF 1111 1111 1 负数

因此得到结论1: 二进制整数的最高位用于充当符号位,符号位为1则表示负数,符号位为0表示正数。

反码

将一个二进制数各个位上的数取反,可得到原数的反码形式,形如下列格式:

原码 反码
0000 0000 1111 1111
0101 1010 1010 0101

取反操作我们用符号表示,对A取反即为~A

若A是一个0到0xFF范围之间的数,由反码的定义我们容易得出如下结论:A + ~A = 0xFF

例:

0000 0000 + 1111 1111 = 1111 1111(0xFF)

0101 1010 + 1010 0101 = 1111 1111(0xFF)

由 A + ~A = 0xFF 可推出:

A + ~A + 1 = 0x100

结论2: ~A + 1 = 0x100 - A

减法转换

原式 B - A,可转换为:

B + (0x100 - A) - 0x100

结论2 可对该式继续转换:

B + (~A + 1) - 0x100 = B - A

由于A,B两数的数据宽度为16位,即范围在0~0xFF范围内的数,而0x100比A、B均高出了1位。

A、B没有额外空间存储高出的一位数据,因此无论进位还是借位都无法处理,所以在16位数据宽度下:

1
B + (~A + 1) - 0x100 = B + (~A + 1)

即: B + (~A + 1) = B - A

原式从减法被转换为了加法。

验证计算结果

接下来我们用8-5这个式子带入进去验证一下(以下算式为显示直观,8位二进制数格式每4位用空格分隔):

8的二进制格式为0000 1000, 5的二进制格式为0000 0101

原式B-A:

0000 1000 - 0000 0101

转换为B + (0x100 - A) - 0x100:

0000 1000 + (1 0000 0000 - 0000 0101) - 1 0000 0000

转换为B + ~A + 1 -0x100:
1
2
3
4
5
0000 1000 + 1111 1010 + 1 - 1 0000 0000

0000 1000 + 1111 1011 - 1 0000 0000

1 0000 0011 - 1 0000 0000 = 0000 0011

0011即十进制的3,8-5=3 成立。

得到结论3:B - A = B + (~A + 1)

求补运算neg()

上面我们论证的的结论3中所定义的操作:

1
~A + 1

该操作被称为求补操作,也即neg(),当我们讨论neg(A)时,实质上就是在讨论 ~A+1

这就是大学中《计算机组成原理》一课中提到的:"求原码的补码时,对负数要取反后加一。"

同理,如果我们从内存中读取出一段数据,将其当作整数类型看待时,那么其真值,也可以根据符号位进行转换。

如果符号位即最高位是0,则将该数看作有符号数时可以做正数看待。

如果符号位即最高位是1,则将该数看作有符号数时当作负数看待,需要通过neg()运算获取其真值

后记

念书时对这些概念完全停留在课本、练习题等层面,完全不理解有什么实际意义。

工作十多年后回过头来看,这些知识其实大有作用。


C语言中的数据存储
http://dubhehub.github.io/blogs/2023121316150051854.html
Author
Sin
Posted on
December 13, 2023
Licensed under