第 12章 位运算
1,位运算符及其应用
2,位段及其应用
12.1位运算符和位运算表达式
C语言中提供了如下 6种的位运算符:
& 按位与
| 按位或
^ 按位异或
<< 左移
>> 右移
~ 按位取反提示:只允许对 整型、字符 型数据进行位运算,而实型数据 不能进行位运算。
1,位运算规则,
例 12.1 若 a=( 15) 10=(00001111)2,
b=(80)10=(01010000)10
则,a&b = 0000 0000,a|b = 0101 1111,
a^b =01011111,~a = 1111 0000
A B A&B A|B A^B ~a
0 0 0 0 0 1
0 1 0 1 1 1
1 0 0 1 1 0
1 1 1 1 0 0
1,位运算的应用:
1) "按位与 "运算经常用于将某些二进制位 屏蔽掉 。
例 12.2 编写函数:使一个整数 k中的低 4位置 0。
分析:用“与”运算实现:将 k的低 4位与 0相与,其他位与 1相与,即将 k与十六进制的 1110 相与。
程序如下:
unsigned int_set(unsigned int k)
{ k=k & 0x1110 ;
return(k); }
结论:任何二进制位与 0相与能实现置 0,与 1相与该位保持不变。
2) "按位或 "运算经常用与将某些二进制位 置 1。
例 12.4:将一个字节的高 4位置为 1010并输出结果。
分析:直接用位运算符,可以很方便的置 1、清 0、翻转等,但无法直接实现置给定的值。可以用两步实现:首先将高 4位清 0,然后与 1010 0000相或。程序如下:
/*设 a 为待处理的数据,转换成二进制为 00001000,
b用来保存将 a的高 4位清 0后的结果 */
main()
{ unsigned char a,b,c; a=8;
b=a&0x0f; c=b|0xa0; /*c用于保存最终结果 */
printf(" %x",c);
}
运行结果,10101000
3) 按位异或运算经常用与将某些位翻转。
例 12.5 编程实现,将一整数 n的高 8位翻转,低 8位不变分析:用,异或” 运算实现,即高 8位与 1相异或,低 8位与 0
相异或,也就是 k与十六进制的 ff00相异或。程序如下:
main()
{ int n=129;
printf(" \n before reset,n=%x",n);
n=n∧ 0xff00;
printf(" \n after reset,n=%x",n);
} 运行结果:
before reset,n=00000000 10000001
after reset,n=11111111 10000001
结论:任何二进制位与 0异或,保持不变;与 1异或,对应位翻转。
4)左移、右移运算实现将一个数的各个二进制位向左、向右移若干位。
左移:将一个数的各个二进制位左移若干位,高位左移后舍弃,低位补 0 。
若定义,int a=8; 即 a= 0000 1000,则语句 a=a<<2;
将 a 的各二进制位左移 2 位,空出的低位补 0。结果为,
0010 0000
右移:将一个数的各个二进制位右移若个位,低位右移后舍弃,高位补 0还是补 1,要区别有符号数还是无符号数:无符号数高位补 0,有符号数高位补原符号位。
若定义 unsigned int a=8; 即 00001000,
则语句 a=a>>2 ; 将 a 的各二进制位右移 2 位,空出的高位补 0。结果为,0000 0010
12.2 位段
1,位段的概念以 位 为单位定义其长度的 结构体成员 。
2.位段的定义例 12.6若某个控制字长 32位,分 5段,每段的长度分别为:
2,6,4,4,16位 ;每段存放一个信息,且第 4段空出不用。可用如下结构体类型描述:
struct con_word
{ unsigned a,2; /*位段 a,长度为 2*/
unsigned b,6 ; /*a,b,c为有名位段 */
unsigned c,4;
unsigned,4; /*该位段为无名位段,表示这 4位空间空出不使用 */
int i ;
} data;
变量 data 的存储结构如图,
a b c i …… 成员名
2 6 4 4 16 …… 长度
3.位段的引用位段是结构体类型的某个成员,因此,位段的引用同结构体成员的引用方法相同。
如,data.a=0x01; /*给位段 a赋值,*/
data.b=0x0a; /*给位段 b赋值 */
data.i=12; /*给位段 i赋值 */
printf(“%02x,%02x,%4d”,data.a,data.b,data.i );
输出,01,0a,12
特别提示:使用位段时,要注意该位段的能表示的数据的范围。
4.位段的应用编程模拟一个温度测控系统:从键盘输入模拟温度的采样值( 0~255),该采样值与 0.2相乘,
得出实际温度值,根据该温度值,控制温度指示灯的亮与灭(从 0度开始,温度每增加 10度则多点亮一个指示灯);将该温度值与设定温度比较,当温度小于 10度和高于 40度时分别开启升温设备和降温设备,同时报警。反复以上过程,直至输入的测温值为 300为止。
分析:系统中需要控制的设备有:五个温度指示灯,升温设备,
降温设备,报警设备。因此可用如下的结构体变量 word表示该系统的控制字,其成员构成如下:
( 1)无符号整型 tem1 用来存放输入的温度采样值( 0~255),
若每个刻度代表 0.2度,则可以测量的温度范围为 0~51度。
浮点类型的成员 tem2用来存放 tem1成员转换成的温度值。
( 2)四个位段存放(要输出的)控制数据。其中,位段 lmp
宽度为 5,控制五个温度指示灯,值为 1时灯亮,为 0时灯不亮。其余的 3个位段 sw1,sw2,alm宽度均为 1,分别控制升温设备、降温设备和报警设备,都是为,1”时开启,为 0时关闭。
函数 sample()实现测温采样:通过键盘输入模拟测温数据,并转换成实际温度值。
函数 control()实现输出控制:通过屏幕显示和喇叭鸣笛的方式输出温度值、温度级别、设备开关情况和报警状态。
#define ON 1
#define OFF 0
struct ctrl_type
{ unsigned char tem1;
float tem2;
unsigned lmp:5;
unsigned sw1:1;
unsigned sw2:1;
unsigned alm:1;
};
main()
{void sample(struct ctrl_type *wp);
void control(struct ctrl_type *wp);
struct ctrl_type word;
while(1)
{sample(&word); /*温度采样 */
/ *生成温度指示灯控制位 */
word.lmp=~(0xffff<<(int)word.tem2/10);
/*生成升温、降温设备及报警设备的控制位 */
if(word.tem2<10)
{word.alm=ON; word.sw1=ON; word.sw2=OFF;}
else if(word.tem2<40)
{word.sw1=word.sw2=word.alm=OFF;}
else {word.sw1=OFF;word.sw2=word.alm=ON;}
control(&word); } } /*输出控制字,控制各设备 */
void sample(struct ctrl_type *wp)
{printf("\ninput test data(0~255)( 300 for end),");
scanf("%u",&wp->tem1);
if(wp->tem1==300) exit(0);
wp->tem2=wp->tem1*0.2;
}
说明:
1)函数 sample()实现测温采样:通过键盘输入模拟测温数据,并转换成实际温度值。
2)函数 control()实现输出控制:通过屏幕显示和喇叭鸣笛的方式输出温度值、温度级别、设备开关情况和报警状态
void control(struct ctrl_type *wp)
{char i,m;
printf("\n%6s %6s %4s %4s %6s","degree","lamp","sw1",
"sw2","alarm");
printf("\n %6.1f ",wp->tem2);
for(i=0,m=0x10;i<5;i++,m>>=1)
printf("%c",wp->lmp&m?'*':' ');
/*用‘ *’表示温度指示灯亮 */
printf("%4s",wp->sw1?"ON":"OFF");
/*用 ON表示开启,用 OFF表示关闭 */
printf("%4s",wp->sw2?"ON":"OFF");
printf("%6s",wp->alm?"ON\x07":"OFF\x20");
}