c位操作.docx

上传人:b****5 文档编号:4956979 上传时间:2022-12-12 格式:DOCX 页数:12 大小:25.47KB
下载 相关 举报
c位操作.docx_第1页
第1页 / 共12页
c位操作.docx_第2页
第2页 / 共12页
c位操作.docx_第3页
第3页 / 共12页
c位操作.docx_第4页
第4页 / 共12页
c位操作.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

c位操作.docx

《c位操作.docx》由会员分享,可在线阅读,更多相关《c位操作.docx(12页珍藏版)》请在冰豆网上搜索。

c位操作.docx

c位操作

c++按位操作符

顾名思义,按位运算符允许按照位来操作整型变量。

可以把按位运算符应用于任意signed和unsigned整型,包括char类型。

但是,它们通常应用于不带符号的整型。

这些运算符的一个常见应用是在整型变量中使用单个的位存储信息。

例如标记,它用于描述二进制状态指示符。

可以使用一个位来描述有两个状态的值:

开或关、男或女,真或假。

也可以使用按位运算符处理存储在一个变量中的几个信息项。

例如,颜色值常常记录为三个八位值,分别存储颜色中红、绿和蓝的强度。

这些常常保存到四字节变量中的三个字节。

第四个字节也不会浪费,包含表示颜色透明度的值。

显然,要处理各个颜色成分,需要从变量中分离出各个字节,按位运算符就可以做到这一点。

再看另外一个例子,假定需要记录字体的信息,那么,只要存储每种字体的样式和字号,以及字体是黑体还是斜体,就可以把这些信息都存储在一个二字节的整型变量中,如图3-1所示。

图3-1把字体数据存储在2个字节中

可以使用一位来记录字体是否为斜体——1表示斜体,0表示一般。

同样,用另一位来指定字体是否为黑体。

使用一个字节可以从多达256种不同的样式中选择一个,再用另外5位记录最多32磅的字号。

因此,在一个16位的字中,可以记录四个不同的数据项。

按位运算符提供了访问和修改整数中单个位和一组位的便利方式,能方便地组合和分解一个16位的字。

3.3.1移位运算符

移位运算符可以把整型变量中的内容向左或向右移动指定的位数。

移位运算符和其他按位运算符一起使用,可以获得前面描述的结果。

>>运算符把位向右移动,<<运算符把位向左移动,移出变量两端的位被舍弃。

所有的按位操作都可以处理任何类型的整数,但本章的例子使用16位的变量,使例子较为简单。

用下面的语句声明并初始化一个变量number:

unsignedshortnumber=16387U;

如第2章所述,不带符号的字面量应在数字的最后添加字母U或u。

在下面的语句中,对这个变量的内容进行移位,并存储结果:

unsignedshortresult=number<<2;//Shiftlefttwobitpositions

移位运算符的左操作数是要移位的值,右操作数指定要移动的位数。

图3-2列出了该操作的过程。

图3-2移位运算

从图3-2可以看出,把数值16387向左移动两位,得到数值12。

数值的这种剧烈变化是舍弃高位数字的结果。

把数值向右移动,可以使用下面的语句:

result=number>>2;//Shiftrighttwobitpositions

把数值16387向右移动两位,得到数值4096。

向右移动两位相当于使该数值除以4。

只要没有舍弃位,向左移动n位就相当于把该数值乘以2的n次方。

换言之,就等于把该数值乘以2n。

同样,向右移动n位就相当于把该数值除以2的n次方。

但要注意,变量number向左移位时,如果舍弃了重要的位,结果就不是我们希望的那样了。

可是,这与乘法运算并没有不同。

如果把2字节的数值乘以4,就会得到相同的结果,所以向左移位和相乘仍是等价的。

出现问题的原因是相乘的结果超出了2字节整数的取值范围。

如果需要修改原来的值,可以使用op=赋值运算符。

在这种情况下,可以使用>>=或<<=运算符。

例如:

number>>=2;//Shiftcontentsofnumbertwopositionstotheright

这等价于:

number=number>>2;//Shiftcontentsofnumbertwopositionstotheright

这些移位运算符跟前面用于输入输出的插入和提取运算符有可能搞混。

从编译器的角度来看,其含义一般可以从上下文中判断出来。

否则,编译器就会生成一个消息,但用户需要非常小心。

例如,如果输出变量number向左移动两位的结果,就应编写下面的代码:

cout<<(number<<2);

其中,括号是必不可少的。

没有括号,编译器就会把移位运算符解释为流插入运算符,从而得不到想要的结果。

按位移动带符号的整数

可以把移位运算符应用于带符号和不带符号的整型数。

但是,向右移位运算符在带符号整数类型的操作随系统的不同而不同,这取决于编译器的实现。

在一些情况下,向右移位运算符会在左边空出来的位上填充0。

在其他情况下,符号位向右移动,在左边空出来的位上填充1。

移动符号位的原因是为了保持向右移位和除法运算的一致性。

可以用char类型的变量来说明这一点,解释其工作原理。

假定把value定义为char类型,其初始值为–104(十进制):

signedcharvalue=–104;

其二进制表示为10011000。

使用下面的操作把它向右移动两位:

value>>=2;//Result11100110

注释中显示了其二进制结果。

右边溢出了两个0,因为符号位是1,就在左边空出来的位上填充1。

该结果的十进制表示是–26,这正好是value的值除以4的结果。

当然,对于不带符号的整数类型的操作,符号位不移动,在左边空出来的位上填充0。

前面说过,在向右移位负整数时,其操作是已定义好的,所以实现该操作时不一定要依赖它。

因为在大多数情况下,使用这些运算符是为了进行按位操作,此时维护位模式的完整性是非常重要的。

所以,应总是使用不带符号的整数,以确保避免高阶位的移位。

3.3.2位模式下的逻辑运算

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

表3-1按位运算符

运算符

说明

~

这是按位求反运算符。

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

&

这是按位与运算符,它对操作数中相应的位进行与运算。

如果相应的位都是1,结果位就是1,否则就是0

^

这是按位异或运算符,它对操作数中相应的位进行异或运算。

如果相应的位各不相同,例如一个位是1,另一个位是0,结果位就是1。

如果相应的位相同,结果位就是0

|

这是按位或运算符,它对操作数中相应的位进行或运算。

如果两个对应的位中有一个是1,结果位就是1。

如果两个位都是0,结果就是0

表3-1中的运算符按照其优先级排列,在这个集合中,按位求反运算符的优先级最高,按位或运算符的优先级最低。

在附录D的运算符优先级表中,按位移动运算符<<和>>具有相同的优先级,它们位于~运算符的下面,&运算符的上面。

如果以前没有见过这些运算符,就会问“这非常有趣,但这是为什么?

”。

下面就将它们用于实践。

1.使用按位与运算符

按位与运算符一般用于选择整数值中特定的一个位或一组位。

为了说明这句话的含义,下面再次使用本节开头的例子,利用一个16位整数存储字体的特性。

假定声明并初始化一个变量,指定一种12磅字号、斜体、样式为6的字体。

实际上,就是图3-1中的字体。

样式的二进制值是00000110,斜体位是1,黑体位是0,字号是01100。

还有一个没有使用的位,需要把font变量的值初始化为二进制数0000011001001100。

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

unsignedshortfont=0x064C;//Style6,italic,12point

注释:

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

要使用字号,需要从font变量中提取它,这可以使用按位与运算符来实现。

只有当两个位都是1时,按位与运算符才会产生1,所以可以定义一个值,在将定义字号的位和font执行按位与操作时选择该位。

为此,只需定义一个值,该值在我们感兴趣的位上包含1,在其他位上包含0。

这种值称为掩码,用下面的语句定义它:

unsignedshortsize_mask=0x1F;//Maskis0000000000011111

//toselectsize

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

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

unsignedshortsize=font&size_mask;

在&操作中,当两个对应的位是1时,结果位就是1。

任何其他组合起来的结果就是0。

因此组合起来的值如下:

font0000011001001100

size_mask0000000000011111

font&size_mask0000000000001100

把二进制值分解为4位一组的形式并不是很重要,这只是易于表示对应的十六进制数,看出其中有多少位。

掩码的作用是把最右边的5位分隔出来,这5位表示点数(即字号)。

可以使用同样的方法选择字体的样式,只是还需要使用按位移动运算符把样式值向右移动。

可以用下面的语句定义一个掩码,选择左边的8位,如下所示:

unsignedshortstyle_mask=0xFF00;//Maskis1111111100000000

//forstyle

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

unsignedshortstyle=(font&style_mask)>>8;//Extractthestyle

该语句的结果如下:

font0000011001001100

style_mask1111111100000000

font&style_mask0000011000000000

(font&style_mask)>>80000000000000110

为表示斜体和黑体的位定义掩码,并把相应的位设置为1,就很容易把它们分隔出来。

当然,还需要一种方式来测试得到的位,这部分内容详见第4章。

按位与运算符的另一个用途是关闭位。

前面介绍的是掩码中为0的位在结果中也将输出0。

例如,为了关闭表示斜体的位,其他的位不变,只需定义一个掩码,使该掩码中的斜体位为0,其他位为1,再对font变量和该掩码进行按位与操作即可。

实现此操作的代码将在按位或运算符一节中介绍。

2.使用按位或运算符

可以使用按位或运算符设置一个或多个位。

继续操作前面的font变量,现在需要设置斜体和黑体位。

用下面的语句可以定义掩码,选择这些位:

unsignedshortitalic=0x40U;//Seventhbitfromtheright

unsignedshortbold=0x20U;//Sixthbitfromtheright

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

font|=bold;//Setbold

位的组合如下:

font0000011001001100

bold0000000000100000

font|bold0000011001101100

现在,font变量指定它表示的字体是黑体和斜体。

注意这个操作会设置位,而不考虑以前的状态。

如果以前位的状态是开,则现在仍保持开的状态。

也可以对掩码执行按位或操作,设置多个位。

下面的语句就同时设置了黑体和斜体:

font|=bold|italic;//Setboldanditalic

该语言很容易让人选择错误的运算符。

“设置斜体和黑体”很容易让人觉得应使用&运算符,而这是错误的。

对两个掩码执行按位与操作会得到一个所有位都是0的值,这不会改变字体的任何属性。

如上一节最后所述,可以使用&运算符关闭位。

也就是定义一个掩码,把其中要关闭的位设置为0,其他位设置为1。

但如何指定这样的掩码?

如果要显式指定它,就需要知道变量中有多少个字节,如果希望程序可以任何方式移植,这就不很方便。

可是,在通常用于打开位的掩码上使用按位求反运算符,就可以得到这样的掩码。

在bold掩码上关闭黑体位,就可以得到该掩码:

bold0000000000100000

~bold1111111111011111

按位求反运算符的作用是反转原数值中的每一位,使0变成1,1变成0。

无论bold变量占用2个字节、4个字节还是8个字节,这都会生成我们期望的结果。

提示:

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

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

font&=~bold;//Turnboldoff

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

例如:

font&=~bold&~italic;//Turnboldanditalicoff

这个语句把font变量中的斜体和黑体位设置为0。

注意这里不需要括号,因为~运算符的优先级高于&运算符。

但是,如果不清楚运算符的优先级,就应加上括号,表示希望执行的操作。

这肯定是无害的,在需要括号时还可以正常发挥作用。

3.使用按位异或运算符

按位异或运算符的使用频率远远低于&和|运算符,有关它的使用例子也比较少。

但它的一个重要应用是图形编程。

在屏幕中创建动画的一种方式是绘制一个对象,删除它,再在一个新位置重新绘制。

如果要求动画很平滑,这个过程就需要重复得很快,其中删除是一个重要的部分。

我们并不想删除和重新绘制整个屏幕,因为这非常费时,屏幕也会出现闪烁。

最理想的是,只删除屏幕上要移动的对象。

使用所谓的异或模式就可以做到这一点,得到非常平滑的动画。

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

如图3-3所示。

图3-3用异或模式绘图

以异或模式在屏幕上绘制对象时,每次绘制对象的颜色会自动在为对象所选的颜色和背景色之间来回变化。

得到这一效果的关键是使用按位异或运算符快速而自动地改变颜色。

它使用异或运算符的一个特性,即如果对两个值进行异或操作,再对所得的结果和一个原始值执行异或操作,就会得到另一个值。

这听起来很复杂,下面就用一个例子来说明。

假定要在前景色(这里使用红色)和背景色(白色)之间来回切换。

颜色通常用3个8位值来表示,分别对应于红、蓝、绿的亮度,存储在一个4字节的整数中。

通过改变颜色中的红、蓝和绿的比例,就可以获得大约1600万种不同的颜色,包括从白色到黑色之间的所有颜色。

纯红色是0xFF0000,这时红色成分设置为其最大值,其他两种颜色即蓝色和绿色的成分设置为0。

在相同颜色模式下,绿色就是0xFF00,蓝色是0xFF。

在白色中,红、蓝、绿的成分具有相同的最大值,即0xFFFFFF。

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

unsignedlongred=0XFF0000UL;//Colorred

unsignedlongwhite=0XFFFFFFUL;//Colorwhite-RGBallmaximum

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

unsignedlongmask=red^white;//Maskforswitchingcolors

unsignedlongdraw_color=red;//Drawingcolor

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

red111111110000000000000000

white111111111111111111111111

mask(即red^white)000000001111111111111111

如果对mask和red执行异或操作,就会得到white,如果对mask和white执行异或操作,就会得到red。

因此,使用draw_color中的颜色绘制对象,就可以通过下面的语句切换颜色:

draw_color^=mask;//Switchthedrawingcolor

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

draw_color111111110000000000000000

mask000000001111111111111111

draw_color^mask111111111111111111111111

显然,draw_color的值从红色变为白色。

再次执行这个语句,就会把颜色改回为红色:

draw_color^=mask;//Switchthedrawingcolor

其执行过程如下:

draw_color111111111111111111111111

mask000000001111111111111111

draw_color^mask111111110000000000000000

draw_color又变成了红色。

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

3.3.2位模式下的逻辑运算

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

表3-1按位运算符

运算符

说明

~

这是按位求反运算符。

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

&

这是按位与运算符,它对操作数中相应的位进行与运算。

如果相应的位都是1,结果位就是1,否则就是0

^

这是按位异或运算符,它对操作数中相应的位进行异或运算。

如果相应的位各不相同,例如一个位是1,另一个位是0,结果位就是1。

如果相应的位相同,结果位就是0

|

这是按位或运算符,它对操作数中相应的位进行或运算。

如果两个对应的位中有一个是1,结果位就是1。

如果两个位都是0,结果就是0

 

表3-1中的运算符按照其优先级排列,在这个集合中,按位求反运算符的优先级最高,按位或运算符的优先级最低。

在附录D的运算符优先级表中,按位移动运算符<<和>>具有相同的优先级,它们位于~运算符的下面,&运算符的上面。

如果以前没有见过这些运算符,就会问“这非常有趣,但这是为什么?

”。

下面就将它们用于实践。

1.使用按位与运算符

按位与运算符一般用于选择整数值中特定的一个位或一组位。

为了说明这句话的含义,下面再次使用本节开头的例子,利用一个16位整数存储字体的特性。

假定声明并初始化一个变量,指定一种12磅字号、斜体、样式为6的字体。

实际上,就是图3-1中的字体。

样式的二进制值是00000110,斜体位是1,黑体位是0,字号是01100。

还有一个没有使用的位,需要把font变量的值初始化为二进制数0000011001001100。

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

unsignedshortfont=0x064C;//Style6,italic,12point

注释:

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

要使用字号,需要从font变量中提取它,这可以使用按位与运算符来实现。

只有当两个位都是1时,按位与运算符才会产生1,所以可以定义一个值,在将定义字号的位和font执行按位与操作时选择该位。

为此,只需定义一个值,该值在我们感兴趣的位上包含1,在其他位上包含0。

这种值称为掩码,用下面的语句定义它:

unsignedshortsize_mask=0x1F;//Maskis0000000000011111

//toselectsize

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

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

unsignedshortsize=font&size_mask;

在&操作中,当两个对应的位是1时,结果位就是1。

任何其他组合起来的结果就是0。

因此组合起来的值如下:

font0000011001001100

size_mask0000000000011111

font&size_mask0000000000001100

把二进制值分解为4位一组的形式并不是很重要,这只是易于表示对应的十六进制数,看出其中有多少位。

掩码的作用是把最右边的5位分隔出来,这5位表示点数(即字号)。

可以使用同样的方法选择字体的样式,只是还需要使用按位移动运算符把样式值向右移动。

可以用下面的语句定义一个掩码,选择左边的8位,如下所示:

unsignedshortstyle_mask=0xFF00;//Maskis1111111100000000

//forstyle

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

unsignedshortstyle=(font&style_mask)>>8;//Extractthestyle

该语句的结果如下:

font0000011001001100

style_mask1111111100000000

font&style_mask0000011000000000

(font&style_mask)>>80000000000000110

为表示斜体和黑体的位定义掩码,并把相应的位设置为1,就很容易把它们分隔出来。

当然,还需要一种方式来测试得到的位,这部分内容详见第4章。

按位与运算符的另一个用途是关闭位。

前面介绍的是掩码中为0的位在结果中也将输出0。

例如,为了关闭表示斜体的位,其他的位不变,只需定义一个掩码,使该掩码中的斜体位为0,其他位为1,再对font变量和该掩码进行按位与操作即可。

实现此操作的代码将在按位或运算符一节中介绍。

2.使用按位或运算符

可以使用按位或运算符设置一个或多个位。

继续操作前面的font变量,现在需要设置斜体和黑体位。

用下面的语句可以定义掩码,选择这些位:

unsignedshortitalic=0x40U;//Seventhbitfromtheright

unsignedshortbold=0x20U;//Sixthbitfromtheright

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

font|=bold;//Setbold

位的组合如下:

font0000011001001100

bold0000000000100000

font|bold0000011001101100

现在,font变量指定它表示的字体是黑体和斜体。

注意这个操作会设置位,而不考虑以前的状态。

如果以前位的状态是开,则现在仍保持开的状态。

也可以对掩码执行按位或操作,设置多个位。

下面的语句就同时设置了黑体和斜体:

font|=bold|italic;//Setboldanditalic

该语言很容易让人选择错误的运算符。

“设置斜体和黑体”很容易让人觉得应使用&运算符,而这是错误的。

对两个掩码执行按位与操作会得到一个所有位都是0的值,这不会改变字体的任何属性。

如上一节最后所述,可以使用&运算符关闭位。

也就是定义一个掩码,把其中要关闭的位设置为0,其他位设置为1。

但如何指定这样的掩码?

如果要显式指定它,就需要知道变量中有多少个字节,如果希望程序可以任何方式移植,这就不很方便。

可是,在通常用于打开位的掩码上使用按

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 军事

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1