无符号(unsigned)编号基于传统的二进制表示法,表示大于或等于零的数字。
补码(two‘s-complement)编码是表示有符号整数的最常见的方式,有符号整数就是可以为正或者为负的数字。
浮点数(floating-point)编码是表示实数的科学记数法的以2为基数的版本。
整数运算和浮点运算会有不同的数学属性是因为它们处理数字表示有限性的方式不同--整数的表示虽然只能编码一个相对较小的数值范围,但是这种表示是精确的;而浮点数虽然可以编码一个较大的数值范围,但是这种表示只是近似的
2.1 信息存储
大多数计算机使用8位的块,或者字节(byte),作为最小的可寻址的内存单位,而不是访问内存中单独的位。机器级程度将内存视为一个非常大的字节数组,称为虚拟内存(virtual memory)。内存的每个字节都由一个唯一的数字来标识,称为它的地址(address),所有可能地址的集合就称为虚拟地址空间。
2.1.1 十六进制表示法
一个字节由8位组成。在二进制表示法中,它的值域是0000000011111111。如果看成十进制整数,它的值域是0255。十六精致使用数字’0‘~’9‘以及字符’A‘~'F'来表示16个可能的值。图2-2展示了16个十六进制数字对应的十进制值和二进制值。用十六进制写,一个字节的值域为00~FF。
练习题 2.1
A. 答: 3 9 A 7 F 8
0011 1001 1010 0111 1111 1000
B. 答: 1100 1001 0111 1011
0x C 9 7 B
C.答:0x D 5 E 4 C
1101 0101 1110 0010 1100
练习题 2.4
A. 0x5045
B.0x4fec
C.0x506f
D.0x50ad
2.1.2 字数据大小
每台计算机都有一个字长(word size),指明指针数据的标称大小。因为虚拟地址是以这样的一个字来编码的,所以字长决定的是最重要的系统就是虚拟地址空间的最大大小。也就是说,对于一个字长为w为的机器而言,虚拟地址的范围是0~2ʷ-1,程序最多访问2ʷ个字节。
 32位字长机器限制虚拟地址空间为4千兆字节(写作4GB),也就是说,刚刚超过4X10⁹字节。扩展到64位字节长度使得虚拟地址空间为16EB,大约是1.84x10¹⁹字节。
2.1.6 布尔代数简介
各运算符的使用:
// 获取0-n之间的所有偶数
func even(a int) (array []int) {
for i := 0; i < a; i++ {
if i&1 == 0 { // 位操作符&与C语言中使用方式一样
array = append(array, i)
}
}
return array
}
// 互换两个变量的值
// 不需要使用第三个变量做中间变量
func swap(a, b int) (int, int) {
a ^= b // 异或等于运算
b ^= a
a ^= b
return a, b
}
// 变换符号
func nagation(a int) int {
// 注意: C语言中是 ~a+1这种方式
return ^a + 1 // Go语言取反方式和C语言不同,Go语言不支持~符号。
}
练习题 2.11
package exclusiveOr
import (
"fmt"
)
func ReverArray(array []*int, cnt int) []*int {
first := 0
last := cnt - 1
aInt := NonPointer(array)
for {
if first > last {
break
}
swap(array[first], array[last])
aInt[first], aInt[last] = swapInt(aInt[first], aInt[last])
print(array)
fmt.Println(aInt)
first++
last--
}
return array
}
func print(array []*int) {
fmt.Println(NonPointer(array))
}
func NonPointer(array []*int) (result []int) {
for _, arg := range array {
result = append(result, *arg)
}
return
}
func swapInt(x, y int) (int, int) {
y = x ^ y
x = x ^ y
y = x ^ y
return x, y
}
func swap(x, y *int) {
*y = *x ^ *y
*x = *x ^ *y
*y = *x ^ *y
}
位级运算的一个常见用法就是掩码运算,这里的掩码是一个位模式,表示从一个字中选出的位的集合。
demo:掩码0xFF(最低的8位为1)表示一个字的低位字节。位级运算x&0xFF生成一个由x的最低有效字节组成的值,而其他的字节就被置为0。比如,对于x=0x89ABCDEF,其表达将得到0x000000EF。表达式~0将生成一个全1的掩码,不管机器的字大小是多少。
练习题 2.12
对于下面的值,写出变量x的C语言表达式。你的代码应该对任何字长w≥8都能工作。我们给出了当x=0x87654321以及w=32时表达式求值的结果,仅供参考。
A. x的最低有效字节,其他位均置为0。[0x00000021]。
B. 除了x的最低有效字节外,其他的位都取补,最低有效字节保持不变。[0x789ABC21]。
C. x的最低有效字节设置成全1,其他字节保持不变。[0x876543FF]。
答案:
A. x & 0xFF
B. x ^ (~0xFF)
表达式~0xFF创建一个掩码,该掩码8个最低位等于0,而其余的位为1,可以观察到,这些掩码的产生和字长无关。而相比之下,表达式0xFFFFFF00只能工作在32位的机器上。
C. x | 0xFF
2.1.8 逻辑运算
C语言还提供了一组逻辑运算符||、&&和!,分别对应于命题逻辑中的OR、AND和NOT运算。逻辑运算很容易和位级运算相混淆,但是它们的功能是完全不同的。逻辑运算认为所有非零的参数都表示TRUE,而参数0表示FALSE。它们返回1或者0,分别表示结果为TRUE或者为FALSE。以下是一些表达式求值的示例。
练习题 2.15
只使用位级和逻辑运算,编写一个C表达式,他等价于x==y。换句话说,当x和y相等时他将返回1,否则返回0
因为 x ^ y 只会在x == y时为0,所以我们可以利用这一性质得到这个表达式其实就是 !(x ^ y)
2.1.9 移位运算
C语言标准并没有明确定义对于有符号数应该使用哪种类型的右移--算术右移或者逻辑右移都可以。不幸地,这就意味着任何假设一种或者另一种右移形式的代码都可能会遇见可移植性问题。然而,实际上,几乎所有的编译器/机器组合都对有符号数使用算术右移,且许多程序员也都假设机器会使用这种右移。另一方面,对于无符号数,右移必须是逻辑的。
Java对于如何右移有明确的定义。表达式x>>k会将x算术右移k个位置,而x>>>k会对x做逻辑右移。
练习题2.16
x | x<<3& | x>>2(逻辑的) | x>>2(算术的)) |
---|---|---|---|
十六进制/二进制 | 二进制/十六进制 | 二进制/十六进制 | 二进制/十六进制 |
0xC3/11000011 | 00011000/0x18 | 00110000/0x30 | 11110000/0xF0 |