从单片机基础到程序框架(全集 2019pdf版).pdf - 第143页

想让第 3 位置 1,其 它位保持不变 ,只需: b=b|(1<<3)。 想让第 4 位置 1,其 它位保持不变 ,只需: b=b|(1<<4)。 想让第 5 位置 1,其 它位保持不变 ,只需: b=b|(1<<5)。 想让第 6 位置 1,其 它位保持不变 ,只需: b=b|(1<<6)。 想让第 7 位置 1,其 它位保持不变 ,只需: b=b|(1<<7)。 分析: 这…

100%1 / 836
20(相当于 5 乘以 2 再乘 2)这个现象背后的规律是:在左移运算中,只要最高位不发生溢出的现象,
那么每左移 1 位就相当于乘 2,左移 2 位相当于乘 2 再乘 2,左移 3 位相当于乘以 2 乘以 2 再乘以
2......以此类推这个规律反过来从乘法的角度看,也是成立的:某个数乘以 2,就相当于左移 1 位,某个
数乘以 2 再乘以 2 相当于左移 2 位,某个数乘以 2 再乘 2 再乘 2 相当于左移 3 位......以此类推那么
问题来了,同样是达到乘以 2 运算结果,从运算速度的角度对比“左移”和“乘法”哪家强?答案是:
一条左移语句的运算速度比一条乘法语句的运算速度要快很多倍。
【34.3 “左移”的常见应用之一:不同数据类型之间的合并。
比如有两 unsigned char 字节的类型数据 H L,H 的初始值是十六进制的 0x12,L 的初始值是
六进制的 0x34,要将两个单字节 H L 合并成一个 unsigned int 双字节的数据 c,其中 H 是高 8 位字节,
L 是低 8 位字节,合并成 c 后,c 的值应该是十六进制的 0x1234,此程序如何写?就需要用到左移。程序
析如下:
unsigned char H=0x12; //单字节
unsigned char L=0x34; //单字节
unsigned int c; //双字节
c=H; //c 的低 8 位被 H 覆盖,也就是 c 的低 8 位得到了 H 的值。
c=c<<8; //及时把 c 的低 8 位移动到高 8 位,同时 c 原来的低 8 位被填入 0
c=c+L; //此时 c 再加 L,c 的低 8 位就 L 的值。
程序运行结果:c 就等于十六进制的 0x1234,十进制 4660。
【34.4 “左移”的常见应用之二:聚焦在某个变量的某个位。
前面第 31 节讲到“或”运算,其中讲到可以对某个变量的某个位置 1,当时是这样讲的,片段如下:
“或”运算最常见的用途是可以指定一个变量的某位置 1,其它位保持不变。比如一个 unsigned char
类型的变量 b,数据长度一共是 8 位,从右往左:
想让第 0 位置 1,其它位保持不变,只需跟十六进制的 0x01 相“或”:b=b|0x01。
想让第 1 位置 1,其它位保持不变,只需跟十六进制的 0x02 相“或”:b=b|0x02。
想让第 2 位置 1,其它位保持不变,只需跟十六进制的 0x04 相“或”:b=b|0x04。
想让第 3 位置 1,其它位保持不变,只需跟十六进制的 0x08 相“或”:b=b|0x08。
想让第 4 位置 1,其它位保持不变,只需跟十六进制的 0x10 相“或”:b=b|0x10。
想让第 5 位置 1,其它位保持不变,只需跟十六进制的 0x20 相“或”:b=b|0x20。
想让第 6 位置 1,其它位保持不变,只需跟十六进制的 0x40 相“或”:b=b|0x40。
想让第 7 位置 1,其它位保持不变,只需跟十六进制的 0x80 相“或”:b=b|0x80。
但是这样写很多程序员会嫌它不直观哪里不直观?就是 0x01,0x02,0x04,0x08,0x10,0x20,0x40,
0x80 这些数不直观,这些数只是代表了聚焦某个变量不同的位。如果把这些十六进制的数值换成左移的写法,
阅读上就清晰了。:0x01 可以 1<<0 代,0x02 以用 1<<1 0x04 1<<2
代......0x80 可以 1<<7 替代。左移的 n ,n 就恰好代表了某个变量的某个位。于是,我们把上面的片
段更改成左移的写法后,如下:
“或”运算最常见的用途是可以指定一个变量的某位置 1,其它位保持不变。比如一个 unsigned char
类型的变量 b,数据长度一共是 8 位,从右往左:
想让第 0 位置 1,其它位保持不变,只需:b=b|(1<<0)。
想让第 1 位置 1,其它位保持不变,只需:b=b|(1<<1)。
想让第 2 位置 1,其它位保持不变,只需:b=b|(1<<2)。
想让第 3 位置 1,其它位保持不变,只需:b=b|(1<<3)。
想让第 4 位置 1,其它位保持不变,只需:b=b|(1<<4)。
想让第 5 位置 1,其它位保持不变,只需:b=b|(1<<5)。
想让第 6 位置 1,其它位保持不变,只需:b=b|(1<<6)。
想让第 7 位置 1,其它位保持不变,只需:b=b|(1<<7)。
分析:这样改进后阅读就很清晰直观了,只是在程序代码的效率速度方面,因为多增加了一条左移
令,意味着要多消耗一条指令的时间,那么到底该选择哪种?其实各有利弊,应该根据个人的编程喜好和
际项目来取舍。很多 32 的单片机在初始化寄存器的库函数里大量应用这种左移的方法来操作,目的就
为了增加代码可读性。
根据上述规律,假设 d 原来等于十进制的 84(十六进制是 0x54,二进制是 01010100)要想把此数据的
0 位置 1,只需 d=d|(1<<0)。最 d 的运算结果是十进制是 85(十六进制 0x55,二进制 01010101)
刚才上面讲到第 31 节的“或”运算,其实在第 30 节的“与”运算中也是可以用这种左移的方法来聚焦,
只是要多配合一条“取反”的指令才可以“与”运算跟“或”运算刚刚相反,它是对某个变量的某个位
零,当时是这样讲的,片段如下:
unsigned char 类型的变量 b,数据长度一共是 8 位,从右往左:
想让第 0 位清零,其它位保持不变,只需跟十六进制的 0xfe 相“与”:b=b&0xfe。
想让第 1 位清零,其它位保持不变,只需跟十六进制的 0xfd 相“与”:b=b&0xfd。
想让第 2 位清零,其它位保持不变,只需跟十六进制的 0xfb 相“与”:b=b&0xfb。
想让第 3 位清零,其它位保持不变,只需跟十六进制的 0xf7 相“与”:b=b&0xf7。
想让第 4 位清零,其它位保持不变,只需跟十六进制的 0xef 相“与”:b=b&0xef。
想让第 5 位清零,其它位保持不变,只需跟十六进制的 0xdf 相“与”:b=b&0xdf。
想让第 6 位清零,其它位保持不变,只需跟十六进制的 0xbf 相“与”:b=b&0xbf。
想让第 7 位清零,其它位保持不变,只需跟十六进制的 0x7f 相“与”:b=b&0x7f。
但是这样写很多程序员会嫌它不直观哪里不直观?就是 0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,
0x7f 这些数不直观,这些数只是代表了聚焦某个变量不同的位。如果把这些十六进制的数值换成左移的写法,
在阅读上就非常清晰直观了,但是注意,这里左移之后还要配一条“取反”语句。比如:0xfe 可以用~(1<<0)
替代,0xfd 可以用~(1<<1)替代,0xfb 可以用~(1<<2)替代......0x7f 可以用~(1<<7)替代移的 n 位后再
取反,n 就恰好代表了某个变量的某个位。于是,我们把上面的片段更改成左移的写法后,如下:
unsigned char 类型的变量 b,数据长度一共是 8 位,从右往左:
想让第 0 位清零,其它位保持不变,只需:b=b&(~(1<<0))。
想让第 1 位清零,其它位保持不变,只需:b=b&(~(1<<1))。
想让第 2 位清零,其它位保持不变,只需:b=b&(~(1<<2))。
想让第 3 位清零,其它位保持不变,只需:b=b&(~(1<<3))。
想让第 4 位清零,其它位保持不变,只需:b=b&(~(1<<4))。
想让第 5 位清零,其它位保持不变,只需:b=b&(~(1<<5))。
想让第 6 位清零,其它位保持不变,只需:b=b&(~(1<<6))。
想让第 7 位清零,其它位保持不变,只需:b=b&(~(1<<7))。
分析:这样改进后阅读就很清晰直观了,只是在程序代码的效率速度方面,因为多增加了一条左移
令和一条取反指令,意味着要多消耗两条指令的时间,那么到底该选择哪种?其实各有利弊应该根据个人
的编程喜好和实际项目来取舍。很多 32 的单片机在初始化寄存器的库函数里大量应用这种左移的方法
操作,目的就是为了增加代码可读性。
根据上述规律,假设 e 原来等于十进制的 85(十六进制是 0x55,二进制是 01010101)要想把此数据的
0 位清零,只需 e=e&(~(1<<0))最终 e 的运算结果是十进制是 84(十六进制是 0x54,二进制是 01010100)
【34.5 左移运算的“左移简写”
当被移数是“保存变量”时,存在“左移简写
“保存变量”=“保存变量”<<n;
上述左移简写如下:
“保存变量”<<=n;
比如:
unsigned char f=1;
unsigned char g=1;
f<<=1; //就相当于 f=f<<1;
g<<=2; //就相当于 g=g<<2;
【34.6 例程练习和分析。
现在编写一个程序来验证刚才讲到的“左移”运算:
程序代码如下:
/*---C 语言学习区域的开始。-----------------------------------------------*/
void main() //主函
{
unsigned char a=5;
unsigned char b=5;
unsigned char H=0x12; //单字节
unsigned char L=0x34; //单字节
unsigned int c; //双字
unsigned char d=84;
unsigned char e=85;
unsigned char f=1;
unsigned char g=1;
//左移运算中蕴含着乘 2 的规律。
a=a<<1; //a 左移 1 位,相当于 a=a*2,从原来的 5 变成了 10。
b=b<<2; //b 左移 2 位,相当于 b=b*2*2,从原来的 5 变成了 20。
//左移的应用之一:不同变量类型的合并