专栏导航
本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏,故划分为两个专栏导航。读者可以自行选择前往哪个专栏。
(一)WIn32 专栏导航
上一篇:编程基础:位运算06,左移
下一篇:编程技能:调试01,调试介绍
(二)MFC 专栏导航
上一篇:编程基础:位运算06,左移
下一篇:编程技能:调试01,调试介绍
本节前言
在上一节,我讲解了左移。本节,我们来讲解右移。
本节,依然会涉及进制转换的知识。
关于进制转换的知识,大家可以参考一下三个链接。
计算机基础:二进制基础04,十进整制数转化为二进制整数-优快云博客
计算机基础:二进制基础13,十六进制与二进制的相互转换-优快云博客
我们开始本节的讲解。
一. 右移
右移,在汇编语言层面,它有算术右移和逻辑右移两种指令,暂时不考虑循环右移。逻辑右移和算术右移,其区别就在于对符号位的处理上。
对于一个无符号数来讲,无所谓符号位。而对于有符号数来讲,最高位是符号位。正整数和 0 的符号位都是 0,而负整数的符号位是 1 。
一个 char 型数,它包含有 8 个二进制位,位 7 为最高位,是符号位。
一个 short 型数,它包含有 16 个二进制位,位 15为最高位,是符号位。
一个 int 型数,它包含有 32 个二进制位,位 31为最高位,是符号位。
以上都属于是有符号类型的数。
右移,它是说,让某一个操作数里面的各个二进制位上的数,向高位移动若干个位。
在 C/C++ 里面,右移运算符为【>>】,不论是逻辑右移还是算术右移,它都是这个运算符。
有了这样的知识,接下来,我们来看右移的语法讲解。
格式:左操作数 >> 右操作数;
效果:左操作数的各个二进制位上的数,向左移动 [右操作数] 个数位。左边移出的数位,在高级语言层面,可以认为是被丢掉了。而左边的空出的数位的处理,根据逻辑右移与算术右移的类型而定。
逻辑右移:当左操作数的数据类型为无符号数时,右移的类型便属于是逻辑右移。对于逻辑右移,左边的空出的数位,要补上 0 。右移 1 位,左边补 1 个 0 。右移 3 位,左边补上 3 个 0 。右移 9 位,左边补上 9 个 0 。
注释:在这里,最左边,指的是左操作数的最高二进制位。比如一个 unsigned short 类型的数,它的最高位,或者是最左边的位,就是位 15 。左边的 3 位,指的是位 15 到位 13 。
算术右移:当左操作数的数据类型是有符号数时,右移的类型便属于是算术右移。对于算术右移来讲,左边的空出的数位,要补上符号位 。正整数和 0 的符号位是 0,在右移时左边补 0 。负整数的符号位是 1,所以在右移时,左边空出的数位要补上 1 。右移 1 位,左边补 1 个符号位。右移 3 位,左边补上 3 个符号位。右移 9 位,左边补上 9 个符号位。
注意事项:移位操作可能引发数据溢出或精度丢失,需谨慎使用。
在移位运算中,移动的位数 没有固定值 ,但实际操作中存在一些规则和限制。比如说,一般会有位数范围限制。移动位数
n
必须满足0 ≤ n < 数据类型位数
。例如,对于32位整数类型(如int
),有效移位范围是0 ≤ n < 32
。尝试移出所有位(如n << 32
)或负数位(如n << -1
)会导致未定义行为或运行时错误。本专栏里面,不讨论大于或等于数据类型位数的移位操作。想了解大于或等于数据类型位数的移位操作,请自行查阅资料。因为,这一块,我也不太懂。遇到了再说吧。在我的经验中,Linux 内核里面存在着移动位数超越数据类型位数的情况,以后遇到了再说。当前阶段里,我们仅仅研究移位的位数小于数据类型位数的情况。
接下来,我们来看一个案例。
char ch01, ch02;
unsigned char ch03;
int num01, num02;
int res01, res02, res03;
int res04, res05, res06;
ch01 = 0xC0;
ch02 = 0x60;
ch03 = 0xE0;
num01 = 1;
num03 = 3;
res01 = ch01 >> num01;
res02 = ch01 >> num02;
res03 = ch02 >> num01;
res04 = ch02 >> num02;
res05 = ch03 >> num01;
res06 = ch03 >> num02;
在这里,ch01 是一个有符号类型的变量,它的值为 0xC0,对应的二进制形式为 1100 0000 。符号位为 1 。
ch02 是一个有符号类型的变量,它的值为 0x60,对应的二进制形式为 0110 0000 。符号位为 0 。
ch03 是一个无符号类型的变量,它的值为 0xE0,对应的二进制形式为 1110 0000 。无符号数没有符号位。
(一)代码 【res01 = ch01 >> num01】的执行情况
ch01 的原值为 1100 0000,num01 的值为 1 。右移 1 位的时候,左边空出的数位要补上符号位,而ch01 的符号位为 1 。所以,右移的结果为 1110 0000 。
(二)代码 【res02 = ch01 >> num02】的执行情况
ch01 的原值为 1100 0000,num02 的值为 3 。右移 3 位的时候,左边空出的数位要补上符号位,而ch01 的符号位为 1 。所以,右移的结果为 1111 1000 。
(三)代码 【res03 = ch02 >> num01】的执行情况
ch02 的原值为 0110 0000,num01 的值为 1 。右移 1 位的时候,左边空出的数位要补上符号位,而ch02 的符号位为 0 。所以,右移的结果为 0011 0000 。
(四)代码 【res04 = ch02 >> num02】的执行情况
ch02 的原值为 0110 0000,num02 的值为 3 。右移 3 位的时候,左边空出的数位要补上符号位,而ch02 的符号位为 0 。所以,右移的结果为 0000 1100 。
(五)代码 【res05 = ch03 >> num01】的执行情况
ch03 的原值为 1110 0000,num01 的值为 1 。右移 1 位的时候,左边空出的数位要补上 0,因为 ch03 是无符号变量 。所以,右移的结果为 0111 0000 。
(六)代码 【res06 = ch03 >> num02】的执行情况
ch03 的原值为 1110 0000,num02 的值为 3 。右移 3 位的时候,左边空出的数位要补上补上 0,因为 ch03 是无符号变量 。所以,右移的结果为 0001 1100 。
这就是右移的基本知识了。
也许,学起来不难。而在讲解之前,在构思的时候,我是觉得不太好讲的。
对于右移,它也存在着它的典型应用,我们一起来看一看。
二. 右移应用,提取 1 个二进制位
关于右移的应用,我这里所了解的一个典型应用是提取某一个数的某几个二进制位。
我们来看一个案例。
unsigned short num;
int res;
num = 0xEC3A;
res = num & (1 << 6);
res >>= 6;
这个案例用到了按位与和左移的知识。如果你对按位与和左移的知识不了解,可以参考以下两个链接所示的文章。
接下来呢,我们来看代码。
(一)代码【num = 0xEC3A;】的执行情况
本行执行完毕,num 会被赋值为 0xEC3A ,它的对应的二进制形式为: 1110 1100 0011 1010
(二)代码【res = num & (1 << 6);】的执行情况
先来看【1 << 6】,1 左移 6 位所形成的数,只有位 6 为 1,其余位都是 0 。num 和这个【只有位 6 为 1,而其余位都是 0】的数相与,其结果就是,num 的其余位均被清零,只有位 6 保留了下来。
与运算的一个典型应用法则是:0 位清零,1 位保留。
这个值会被赋予 res 之中。
(三)代码【res >>= 6;】的执行情况
在执行本行代码之前,res 的其余位均被清零,而位 6 则是保留了 num 的位 6 的原值。
执行了以后,则 位 6 被移到位 0,而其余位均为 0 。
(四)本案例的总体含义
本案例代码,先是令 num 中的无关位清零,只保留位 6,然后将位 6 移到位 0 。所以,本案例的总体的含义是,将 num 中的位 6单独提取出来。
三. 右移应用,提取多个二进制位
我们来看一个案例。
unsigned short num;
int res;
num = 0xEC3A;
res = num & (7 << 6);
res >>= 6;
这个案例与上一分节的案例差不多,只是将【res = num & (1 << 6);】修改为了【res = num & (7 << 6);】而已。我们来看代码。
(一)代码【num = 0xEC3A;】的执行情况
本行执行完毕,num 会被赋值为 0xEC3A ,它的对应的二进制形式为: 1110 1100 0011 1010 。
(二)代码【res = num & (7 << 6);】的执行情况
先来看【7 << 6】,7 的二进制形式为 111,,它左移 6 位所形成的数,只有位 8 到位 6 三位为 1,其余位都是 0 。num 和这个【只有位 8 到位 6 三位为 1,而其余位都是 0】的数相与,其结果就是,num 的其余位均被清零,只有位 8 到位 6 保留了下来。
与运算的一个典型应用法则是:0 位清零,1 位保留。
这个值会被赋予 res 之中。
(三)代码【res >>= 6;】的执行情况
在执行本行代码之前,res 的其余位均被清零,而位 8 到位 6 则是保留了 num 的位 8 到位 6 的原值。
执行了以后,则 位 8 到位 6 被移到位 2 到位 0,而其余位均为 0 。
(四)本案例的总体含义
本案例代码,先是令 num 中的无关位清零,只保留位 8 到位 6,然后将位 8 到位 6 移到位 0 。所以,本案例的总体的含义是,将 num 中的位 8 到位 6单独提取出来。
四. 右移应用总结
在本篇文章里面,我总结了右移应用的一个典型应用,提取某一个数的一个或多个二进制位。
以后,你有可能会接触到右移的更多的应用。这一点,你自己慢慢积累经验吧。
结束语
讲解右移知识,对我来讲,还是有一定的挑战性的。
想要将一个知识给讲清楚,有时,也是会有困难的。
希望大家能够学好本节的知识。
关于左移和右移的知识,你还需要自己去测试一些个东西。本节,我就先不讲这个测试知识了。
本节结束。
专栏导航
本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏,故划分为两个专栏导航。读者可以自行选择前往哪个专栏。
(一)WIn32 专栏导航
上一篇:编程基础:位运算06,左移
下一篇:编程技能:调试01,调试介绍
(二)MFC 专栏导航
上一篇:编程基础:位运算06,左移
下一篇:编程技能:调试01,调试介绍