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) = 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 |
|
0011即十进制的3,8-5=3 成立。
得到结论3:B - A = B + (~A + 1)
求补运算neg()
上面我们论证的的结论3中所定义的操作:
1 |
|
该操作被称为求补操作,也即neg(),当我们讨论neg(A)时,实质上就是在讨论 ~A+1
这就是大学中《计算机组成原理》一课中提到的:"求原码的补码时,对负数要取反后加一。"
同理,如果我们从内存中读取出一段数据,将其当作整数类型看待时,那么其真值,也可以根据符号位进行转换。
如果符号位即最高位是0,则将该数看作有符号数时可以做正数看待。
如果符号位即最高位是1,则将该数看作有符号数时当作负数看待,需要通过neg()运算获取其真值。
后记
念书时对这些概念完全停留在课本、练习题等层面,完全不理解有什么实际意义。
工作十多年后回过头来看,这些知识其实大有作用。