深入理解《深入理解计算机系统(CSAPP)》
一、进度
第二章(2/4)
二、关键词
布尔代数、位级运算、逻辑运算、移位运算
三、笔记
1、布尔代数
上述的四种布尔运算可以扩展到位向量(固定长度为ω,由0和1组成的串),对于位向量的运算可以定义成参数的每个对应元素之间的运算。
布尔代数和整数算术运算有很多相似之处。例如乘法对加法有分配律,写为
a
∗
(
b
+
c
)
=
(
a
∗
b
)
+
(
a
∗
c
)
a*(b+c)=(a*b)+(a*c)
a∗(b+c)=(a∗b)+(a∗c)
而布尔运算 & 对 | 也具有分配律,写为
a&(b|c)=(a&b)|(a&c)
需要注意的是,布尔运算 | 对 & 也有分配律,这与整数是不同的
a|(b&c)=(a|b)&(a|c)
当考虑长度为ω的位向量上的 ^ 、&和~运算时,会得到一种不同的数学形式,称为布尔环(Boolean ring)。布尔环与整数运算有很多相同的属性。如,整数运算的每一个值x都有一个加法逆元(additive inverse) -x,使得x+(-x)=0。布尔运算中的^也有类似的性质,a ^ a=0。这个结论扩展到位向量也是成立的,因此(ab)a=b。
位向量一个很有用的应用就是表示集合。我们可以用位向量[a(ω-1),…,a(1),a(0)]编码任何子集A⊆{0,1,…,ω-1},其中a( i )=1当且仅当 i ∈A,即通过位向量中1出现的位数来代表集合中的数字。如a=[01101001]表示集合A={0,3,5,6}。运用这种编码集合的方式,布尔运算中的 | 和&恰好对应集合的并和交,而~对应集合的补。掩码是该编码方式的一个典型应用场景。
2、C语言中的位级运算
C语言一个重要的特性就是对位级运算的支持。
在c语言中,我们能通过位级运算实现仅需两个变量的值交换,尽管这种交换方式并没有性能上的优势。
void inplace_swap(int* x, int* y)
{
*y = *x ^ *y;
*x = *x ^ *y;
*y = *x ^ *y;
}
位级运算的另一个常见用法就是实现掩码运算,这里掩码指一种位模式,表示从一个字中选出的位的集合。如,掩码0xFF表示一个字的低位字节,位级运算x & 0xFF生成x最低有效字节的值。
3、c语言中的逻辑运算
C语言提供一组逻辑运算符||、&&和!。
逻辑运算符&&和||与它们对应的位级运算符重要的区别是,如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。
4、c语言中的移位运算
对于左移运算,x<<k,表示x向左移动k位,丢弃最高的k位,并在右端补k个0
对于右移运算,x>>k,分为逻辑右移与算术右移两类。逻辑右移在左端补k个0,算术右移在左端补k个最高有效位的值。
对无符号数,必须使用逻辑右移。但C语言并未明确定义对有符号数应该使用哪种类型的右移。然而,几乎所有的编译器/机器组合都对有符号数使用算术右移,程序员也都假设机器会使用这种右移。而Java对如何进行右移有明确规定。x>>k表明使用算术右移,x>>>k表示逻辑右移。
一个由ω位组成的数据类型,如果移动k>=ω会得到什么结果呢?在许多机器上,当移动一个ω位的值,移位指令值考虑位移量的低log2(ω)位,即实际位移量是通过计算k mod w得到的。但这种行为对于C程序来说是没有保证的,所以应该保持位移量小于待移位值的位数。
四、习题
1、掩码
解答:A、x & 0xFF B、x ^ ~0xFF C、x | 0xFF
反思:对于单个位运算,| 0、&1意味着保持位不变,&0意味着将位设置为0,| 1意味着将位设置为1。但该练习题的核心在于可移植性的实现。对于B问题,~0xFF创建了一个字长不限、最低位为0的掩码,使得代码具有可移植性。
2、布尔运算实现机制
解答:1、bis(x,y) 2、bis(bic(x,y),bic(y,x))
反思:根据题意可知,bis(x,y)等效于x | y,bic(x,y)等效于x&y。由于x^y=(x&y)|(~x&y),可得出答案。