位模式下的逻辑运算(转)

位模式下的逻辑运算

修改整数值中的位时,可以使用4个按位运算符,如表3-1所示。

表1按位运算符

运 算 符

说 明

~

这是按位求反运算符。它是一个一元运算符,可以反转操作数中的位,即1变成0,0变成1

&

这是按位与运算符,它对操作数中相应的位进行与运算。如果相应的位都是1,结果位就是1,否则就是0

^

这是按位异或运算符,它对操作数中相应的位进行异或运算。如果相应的位各不相同,例如一个位是1,另一个位是0,结果位就是1。如果相应的位相同,结果位就是0

|

这是按位或运算符,它对操作数中相应的位进行或运算。如果两个对应的位中有一个是1,结果位就是1。如果两个位都是0,结果就是0

 

表1中的运算符按照其优先级排列,在这个集合中,按位求反运算符的优先级最高,按位或运算符的优先级最低。在附录D的运算符优先级表中,按位移动运算符<<和>>具有相同的优先级,它们位于~运算符的下面,&运算符的上面。

如果以前没有见过这些运算符,就会问“这非常有趣,但这是为什么?”。下面就将它们用于实践。

1. 使用按位与运算符

按位与运算符一般用于选择整数值中特定的一个位或一组位。为了说明这句话的含义,下面再次使用本节开头的例子,利用一个16位整数存储字体的特性。

假定声明并初始化一个变量,指定一种12磅字号、斜体、样式为6的字体。实际上,就是图3-1中的字体。样式的二进制值是00000110,斜体位是1,黑体位是0,字号是01100。还有一个没有使用的位,需要把font变量的值初始化为二进制数0000 0110 0100 1100。

由于4位二进制数对应于一个16进制数,因此最简单的方法是以十六进制方式指定初 始值:

unsigned short font=0x064C; // Style 6, italic, 12 point

注释:

在建立像这样的位模式时,十六进制表示法要比十进制表示法更合适。

要使用字号,需要从font变量中提取它,这可以使用按位与运算符来实现。只有当两个位都是1时,按位与运算符才会产生1,所以可以定义一个值,在将定义字号的位和font执行按位与操作时选择该位。为此,只需定义一个值,该值在我们感兴趣的位上包含1,在其他位上包含0。这种值称为掩码,用下面的语句定义它:

unsigned short size_mask=0x1F; //Mask is 0000 0000 0001 1111

//to select size

font变量的5个低位表示其字号,把这些位设置为1,剩余的位设置为0,这样它们就会被舍弃(二进制数0000 0000 0001 1111可转换为十六进制数1F)。

现在可以用下面的语句提取font中的字号了:

unsigned short size=font & size_mask;

在&操作中,当两个对应的位是1时,结果位就是1。任何其他组合起来的结果就是0。因此组合起来的值如下:

font 0000 0110 0100 1100

size_mask 0000 0000 0001 1111

font & size_mask 0000 0000 0000 1100

把二进制值分解为4位一组的形式并不是很重要,这只是易于表示对应的十六进制数,看出其中有多少位。掩码的作用是把最右边的5位分隔出来,这5位表示点数(即字号)。

可以使用同样的方法选择字体的样式,只是还需要使用按位移动运算符把样式值向右移动。可以用下面的语句定义一个掩码,选择左边的8位,如下所示:

unsigned short style_mask=0xFF00; //Mask is 1111 1111 0000 0000

//for style

用下面的语句获取样式值:

unsigned short style=(font & style_mask) >> 8; //Extract the style

该语句的结果如下:

font 0000 0110 0100 1100

style_mask 1111 1111 0000 0000

font & style_mask 0000 0110 0000 0000

(font & style_mask) >> 8 0000 0000 0000 0110

为表示斜体和黑体的位定义掩码,并把相应的位设置为1,就很容易把它们分隔出来。当然,还需要一种方式来测试得到的位,这部分内容详见第4章。

按位与运算符的另一个用途是关闭位。前面介绍的是掩码中为0的位在结果中也将输出0。例如,为了关闭表示斜体的位,其他的位不变,只需定义一个掩码,使该掩码中的斜体位为0,其他位为1,再对font变量和该掩码进行按位与操作即可。实现此操作的代码将在按位或运算符一节中介绍。

2. 使用按位或运算符

可以使用按位或运算符设置一个或多个位。继续操作前面的font变量,现在需要设置斜体和黑体位。用下面的语句可以定义掩码,选择这些位:

unsigned short italic=0x40U; //Seventh bit from the right

unsigned short bold=0x20U; //Sixth bit from the right

用下面的语句设置黑体位:

font |= bold; // Set bold

位的组合如下:

font 0000 0110 0100 1100

bold 0000 0000 0010 0000

font | bold 0000 0110 0110 1100

现在,font变量指定它表示的字体是黑体和斜体。注意这个操作会设置位,而不考虑以前的状态。如果以前位的状态是开,则现在仍保持开的状态。

也可以对掩码执行按位或操作,设置多个位。下面的语句就同时设置了黑体和斜体:

font |= bold | italic; //Set bold and italic

该语言很容易让人选择错误的运算符。“设置斜体和黑体”很容易让人觉得应使用&运算符,而这是错误的。对两个掩码执行按位与操作会得到一个所有位都是0的值,这不会改变字体的任何属性。

如上一节最后所述,可以使用&运算符关闭位。也就是定义一个掩码,把其中要关闭的位设置为0,其他位设置为1。但如何指定这样的掩码?如果要显式指定它,就需要知道变量中有多少个字节,如果希望程序可以任何方式移植,这就不很方便。可是,在通常用于打开位的掩码上使用按位求反运算符,就可以得到这样的掩码。在bold掩码上关闭黑体位,就可以得到该掩码:

bold 0000 0000 0010 0000

~bold 1111 1111 1101 1111

按位求反运算符的作用是反转原数值中的每一位,使0变成1,1变成0。无论bold变量占用2个字节、4个字节还是8个字节,这都会生成我们期望的结果。

提示:

按位求反运算符有时称为NOT运算符,因为对于它操作的每个位,都会得到跟开始不同的值。

因此,在关闭黑体位时,只需对掩码bold的反码和font变量执行按位与操作,可用的语句如下所示:

font &= ~bold; //Turn bold off

还可以使用&运算符把几个掩码组合起来,再对结果跟要修改的变量执行按位与操作,将多个位设置为0。例如:

font &= ~bold & ~italic; //Turn bold and italic off

这个语句把font变量中的斜体和黑体位设置为0。注意这里不需要括号,因为~运算符的优先级高于&运算符。但是,如果不清楚运算符的优先级,就应加上括号,表示希望执行的操作。这肯定是无害的,在需要括号时还可以正常发挥作用。

3. 使用按位异或运算符

按位异或运算符的使用频率远远低于&和 | 运算符,有关它的使用例子也比较少。但它的一个重要应用是图形编程。在屏幕中创建动画的一种方式是绘制一个对象,删除它,再在一个新位置重新绘制。如果要求动画很平滑,这个过程就需要重复得很快,其中删除是一个重要的部分。我们并不想删除和重新绘制整个屏幕,因为这非常费时,屏幕也会出现闪烁。最理想的是,只删除屏幕上要移动的对象。使用所谓的异或模式就可以做到这一点,得到非常平滑的 动画。

异或模式的理念是,在屏幕上用给定的颜色绘制对象,如果接着用背景色重新绘制它,它就会消失。

 

 

以异或模式在屏幕上绘制对象时,每次绘制对象的颜色会自动在为对象所选的颜色和背景色之间来回变化。得到这一效果的关键是使用按位异或运算符快速而自动地改变颜色。它使用异或运算符的一个特性,即如果对两个值进行异或操作,再对所得的结果和一个原始值执行异或操作,就会得到另一个值。这听起来很复杂,下面就用一个例子来说明。

假定要在前景色(这里使用红色)和背景色(白色)之间来回切换。颜色通常用3个8位值来表示,分别对应于红、蓝、绿的亮度,存储在一个4字节的整数中。通过改变颜色中的红、蓝和绿的比例,就可以获得大约1600万种不同的颜色,包括从白色到黑色之间的所有颜色。纯红色是0xFF0000,这时红色成分设置为其最大值,其他两种颜色即蓝色和绿色的成分设置为0。在相同颜色模式下,绿色就是0xFF00,蓝色是0xFF。在白色中,红、蓝、绿的成分具有相同的最大值,即0xFFFFFF。

可以用下面的语句定义表示红色和白色的变量:

unsigned long red=0XFF0000UL; //Color red

unsigned long white=0XFFFFFFUL; //Color white-RGB all maximum

接着创建一个掩码,用于在红色和白色之间来回切换,并把包含绘图颜色的变量初始化为红色:

unsigned long mask=red ^ white; //Mask for switching colors

unsigned long draw_color=red; //Drawing color

变量mask初始化为要切换的两种颜色的按位异或操作结果,因此:

red 1111 1111 0000 0000 0000 0000

white 1111 1111 1111 1111 1111 1111

mask(即red ^ white) 0000 0000 1111 1111 1111 1111

如果对mask和red执行异或操作,就会得到white,如果对mask和white执行异或操作,就会得到red。因此,使用draw_color中的颜色绘制对象,就可以通过下面的语句切换颜色:

draw_color ^= mask; //Switch the drawing color

当draw_color包含red时,其执行过程如下:

draw_color 1111 1111 0000 0000 0000 0000

mask 0000 0000 1111 1111 1111 1111

draw_color ^ mask 1111 1111 1111 1111 1111 1111

显然,draw_color的值从红色变为白色。再次执行这个语句,就会把颜色改回为红色:

draw_color^=mask; //Switch the drawing color

其执行过程如下:

draw_color 1111 1111 1111 1111 1111 1111

mask 0000 0000 1111 1111 1111 1111

draw_color ^ mask 1111 1111 0000 0000 0000 0000

draw_color又变成了红色。这个技术适用于任意两种颜色,当然它实际上与特定颜色没有一点关系,可以把它用于切换任意一对整型数值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值