原码、反码、补码再探
概述
三个计算机用来表达负数的形式。
- 原码
- 通过第一位的 \(0\) 来直接表示正数,\(1\) 来直接表示负数。
- 然而计算机并不用这种方式。
- 反码
- 即把要表示的负数的绝对值对应的二进制全部取反来表示。
- 坏处是 \(0\) 有两种表达方式,全 \(0\) 和全 \(1\) ,所以也不常用。
- 补码
- 即把要表示的负数的绝对值对应的二进制全部取反后再加一来表示。
- 计算机用这种方式就很方便。
转换
转出
对于 \(-57\)
\(57\) 的二进制:
\(0011\space1001\)
- 原码
- \(1011\space1001\)
- 反码
- \(1100\space 0110\)
- 补码
- \(1100\space 0111\)
转回
- 原码 \(1011\space 1001\)
- \(-0011\space 1001 = -57\)
- 反码 \(1100\space 0110\)
- 取反加一
- \(-2^7 + 2^6 + 2^2 + 2^1 = -58\)
- \(-58 + 1 = -57\)
- 补码 \(1100\space 0111\)
- 直接取反
- \(-2^7 + 2^6 + 2^2 +2^1 + 2^0 = -57\)
计算
计算机计算二进制减法:
\(66-57 = 9\)
\(67 = 0100\space0010\)
\(-57 = 1100\space 0111\)
\[\space\space\space 0100\space0010 \]\[+1100\space0111 \]\[\space10000\space1001 \]这个数字溢出了 \(1\) 位,所以去低位数的 \(8\) 位,得到的答案就是 \(0000\space1001\),也就是十进制下的 \(9\) 。
补码的设计使得计算机可以化减为加。
但谈论补码时,务必要确定总位数,如 char
类型的 \(1100\space0111\) 和 int
类型的 \(0\dots0\space 1100\space 0111\) 表示的可不是同一个数字。
\(memset\)
到这里就可以解释为什么memset
只对于 \(-1\) 和 \(0\) 有一一对应的赋值。
首先 memset
按字节赋值,即对于每个字节赋予你给定的值。
对于 \(0\) ,在补码体系下,每个字节都是 \(0\),所以对应。
对于 \(-1\),补码体系下,对于一个字节 \(1111\space1111\),每个字节都是 \(1111\space1111\),所以对应。
依照原理,memset
\(0xff\) 即 \(1111\space 1111\) 自然也可以把数组初始化为 \(-1\)。