430 likes | 654 Views
C 语言程序设计. 主讲:沈济南. TEL: 13971887071 E-mail: shenjinan@163.com. 第十九讲. 主讲内容: 第九章 位运算 9.1 位运算符与位运算 9.2 位段 9.3 程序设计举例. 9.1 位运算符与位运算. 9.1.1 位运算符. 9.1.2 按位取反运算符. 按位取反运算符“~”是一个单目运算符,能对一个二进制数的每一位都取反,即 0 变为 1 , 1 变为 0 。 例如: a= 0 0 0 1 1 0 1 0 /* 十六进制为 1a*/
E N D
C语言程序设计 主讲:沈济南 TEL:13971887071 E-mail:shenjinan@163.com
第十九讲 • 主讲内容: • 第九章 位运算 • 9.1 位运算符与位运算 • 9.2 位段 • 9.3 程序设计举例
9.1.2 按位取反运算符 • 按位取反运算符“~”是一个单目运算符,能对一个二进制数的每一位都取反,即0变为1,1变为0。 • 例如: • a= 0 0 0 1 1 0 1 0 /*十六进制为1a*/ • ~ a= 1 1 1 0 0 1 0 1 /*十六进制为e5*/
9.1.3 左移运算符 • 左移运算符“<<”的功能是将一个数的各个二进制位全部向左平移若干位,左边移出的部分予以忽略,右边空出的位置补零。如: • a = 0 0 0 1 1 0 1 0 /*十六进制为1a*/ • a<<2 = 0 1 1 0 1 0 0 0 /*十六进制为68*/ • 一个数据,每左移1位相当于乘以2,左移2位相当于乘以4,以此类推。
9.1.4 右移运算符 • 与左移相反,右移运算符“>>”的功能是将一个数的各个二进制位全部向右平移若干位,右边移出的部分予以忽略,左边空出的位置对于无符号数补零,对于有符号数,若原符号位为0,则补0,若原符号位为1,则全补1。也就是右移后保持这个数的正负符号不变。 • 例如,若变量a被定义成unsigned char,即无符号型,则有: • a = 1 0 0 1 1 0 1 0 /*十六进制为9a*/ • a>>2 = 0 0 1 0 0 1 1 0 /*十六进制为26*/
若变量a被定义成char,即有符号型,则有: • a = 1 0 0 1 1 0 1 0 /*十六进制为9a*/ • a>>2 = 1 1 1 0 0 1 1 0 /*十六进制为e6*/ • 同样,一个数据每右移1位相当于除以2,右移2位相当于除以4,以此类推。
9.1.5 按位与运算符 • 运算符“&”将其两边数据对应的各个二进制位分别进行“与”运算,即二者都为1时结果为1,否则为0。如: • a = 1 0 1 1 1 0 1 0 /*十六进制为ba*/ • b = 0 1 1 0 1 1 1 0 /*十六进制为6e*/ • a&b = 0 0 1 0 1 0 1 0 /*十六进制为2a*/ • 可以发现,任何一位与1“与”运算时,结果保持原值,与0“与”运算时,结果皆为0。
如果参加&运算的是负数(如-3&-5),则以补码形式表示为二进制数,然后按位进行“与”运算。如果参加&运算的是负数(如-3&-5),则以补码形式表示为二进制数,然后按位进行“与”运算。 • 按位与有一些特殊的用途: • (1)清零。如果想将一个数的某些位清零,只要找一个二进制数,其中相应位为0,然后使二者进行&运算,即可达到清零目的。 • 如:原有数为00101011,现使它低四位清零。另找一个数,设它为10010000,低四位均为0。将两个数进行&运算:
00101011 • (&) 10010000 • 00000000 • 其道理是显然的。当然也可以不用10010000这个数而用其他数(如01000000)也可以。 • (2)保留一个数中某些指定位。如果想保留一个数中某些指定位,只要找一个二进制数,其中相应位为1,然后使二者进行&运算,即可达到目的。如有一个整数a(2个字节),想要其中的低字节。只需将a与(377)o按位与即可。
【例9.1】将一个十进制数转换为二进制数。 • 分析:C语言中printf函数提供的%x、%d、%o格式符可将一个整数以十六进制、十进制或八进制的形式输出,但没有二进制输出格式。人工转换的方法是设置一个屏蔽字,其中只有一位是1,其余各位均为0,与被转换数进行“与”运算,根据运算结果判断被测试的那一位是1还是0,其余二进位的测试方法相同。一个整数占4个字节,共有32个二进制位。
/*源程序名:CH0901.C*/ • /*01*/ #include<stdio.h> • /*02*/ void main() • /*03*/ { • /*04*/ int i,bit; • /*05*/ unsigned int n,mask; • /*06*/ mask=0x80000000; /*最高位为1,其余位为0*/ • /*07*/ printf("enter your number:"); • /*08*/ scanf("%d",&n); • /*09*/ printf("binary of %d is:",n);
/*10*/ for(i=0;i<32;i++) • /*11*/ { • /*12*/ bit=(mask & n)?1:0; • /*13*/ printf("%1d",bit); • /*14*/ mask=mask>>1; /*右移一位,得到下一个屏蔽字*/ • /*15*/ } • /*16*/ } • 程序运行过程: • enter your number: • 8↙ • binary of 8 is:00000000000000000000000000001000
9.1.6 按位或运算符 • 运算符“|”将两边对应的二进制位分别进行“或”运算,即二者之中只要有一个为1时结果就为1,两者都为0时结果才为0。如: • a = 1 0 0 1 1 0 1 0 /*十六进制为9a*/ • b = 0 1 0 1 0 1 1 0 /*十六进制为56*/ • a|b = 1 1 0 1 1 1 1 0 /*十六进制为de*/ • 可以发现,任何一位与0“或”时,其结果就等同于这一位。 • 按位或有一些特殊的用途: • (1)将一个数中某些指定位置1。
只要找一个二进制数,其中相应位为1,然后使二者进行或运算,即可达到目的。只要找一个二进制数,其中相应位为1,然后使二者进行或运算,即可达到目的。 • (2)保留一个数中某些指定位。如果想保留一个数中某些指定位,只要找一个二进制数,其中相应位为0,然后使二者进行或运算,即可达到目的。
9.1.7 按位异或运算符 • 按位异或运算符“^”的作用是判断两个相应位的值是否“相异”(不同),若为异,则结果为1,否则为0。如: • a = 1 0 0 1 1 0 1 0 /*十六进制为9a*/ • b = 0 1 0 1 0 1 1 0 /*十六进制为56*/ • a^b = 1 1 0 0 1 1 0 0 /*十六进制为cc*/ • 可以发现,任何一位与1“异或”时,其结果是将这一位取反,即由1变成0,或者由0变成1。 • 按位异或运算符的一些特殊的用途:
(1) 使特定位翻转 • 假设有01111010,想使其低4位翻转,即1变为0,0变为1。可以将它与 • 00001111进行∧运算,即 • 01111010 • (∧) 00001111 • 01110101 • 结果值的低4位正好是原数低4位的翻转。要使哪几位翻转就将与其进行∧运算的该几位置为1即可。这是因为原数中值为1的位与1进行∧运算得0,原数中的位值0与1进行∧运算的结果得1。
(2) 与0相∧,保留原值 • 如012∧00=012 • 00001010 • (∧)00000000 • 00001010 • 因为原数中的1与0进行∧运算得1,0∧0得0,故保留原数。 • (3) 交换两个值,不用临时变量 • 假如a=3,b=4。想将a和b的值互换,可以用以下赋值语句实现: • a=a∧b; • b=b∧a; • a=a∧b;
9.2.1 位段结构体说明 • 位段结构体说明的一般形式为: • struct 位段结构体名 • { • unsigned int [位段名1]:k1; • unsigned int [位段名2]:k2; • …… • unsigned int [位段名n]:kn; • }; • 其中k1,k2,…,kn,一般是0—9中的一个数,表示某位段成员占的二进制位数(位段的宽度)。
例如: • structpacked-data • { • unsigneda:2; • unsignedb:6; • unsignedc:4; • unsignedd:4; • inti; • }data; • 其中a、b、c、d分别占2位、6位、4位、4位。i为整型。共占2个字节。也可以使各个位段不恰好占满一个字节。如:
structpacked-data • { • unsigneda:2; • unsignedb:3; • unsignedc:4; • inti; • }; • structpacked-datadata; • 其中a、b、c共占9位,占1个字节多,不到2个字节。它的后面为int型,占2个字节。在a、b、c之后7位空间闲置不用,i从另一字节开头起存放。 • 位段成员的类型只能是int 或unsigned int;在每个字段说明的最后需要给出该字段的二进制位数,任一字段的位数不能超过一个字(一个int)的长度。
9.2.2 位段的引用 • 对位段的引用方法与引用结构体变量中的成员完全相同。 • 用位段结构体变量和成员访问运算符来引用位段。如: • x.a=10; x.c=2; • 注意:对位段赋值时,应不超过每一个位段能存储的最大值(由位段的宽度决定的,如上例中x.b的最大值为3,x.a的最大值为15),否则会溢出。 • 也可以用指针变量指向一个成员为位段的结构体变量,然后通过该指针变量来引用位段。如: • struct packed_data x, *p; • p=&x; /* 使p指向x */ p->a=10; p->c=2;
【例9.2】按位异或运算可用来实现交换两个变量的值(并且不用第三个变量)。【例9.2】按位异或运算可用来实现交换两个变量的值(并且不用第三个变量)。 • /*源程序名:CH0902.C*/ • /*01*/ #include<stdio.h> • /*02*/ void main() • /*03*/ { • /*04*/ int a , b ; • /*05*/ a=10; • /*06*/ b=20; • /*07*/ printf(“a=%d\tb=%d\n”,a, b); • /*08*/ a=a^b; • /*09*/ b=b^a;
/*10*/ a=a^b; • /*11*/ printf(“a=%d\tb=%d\n”,a, b); • /*12*/ } • 运行结果为:a=10 b=20 • a=20 b=10
【例9.3】机器中有一个机器字称为处理机状态字,它由若干字段组成,它反映处理机运行的状态。假设第5~7位是处理机的优先级,有时需要改变优先级,就必须取出第5~7位。设计一个程序取出处理机状态字的优先级。【例9.3】机器中有一个机器字称为处理机状态字,它由若干字段组成,它反映处理机运行的状态。假设第5~7位是处理机的优先级,有时需要改变优先级,就必须取出第5~7位。设计一个程序取出处理机状态字的优先级。 如下:(图)
/*源程序名:CH0903.C*/ • /*01*/ #include<stdio.h> • /*02*/ void main() • /*03*/ { • /*04*/ unsigned int ps=0170360; • /*05*/ int p, n, t ; • /*06*/ p=7; /*开始位置*/ • /*07*/ n=3; /*位数*/ • /*08*/ ps=ps>>(p-n+1); /*将ps的5~7位右移到0~2位*/ • /*09*/ t=~(~0<<n); /*将3~15位置为0,0~2位置为1*/ • /*10*/ ps=ps&t; /*取出需要的位*/
/*11*/ printf(“ps=%o\n”,ps); • /*12*/ } • 运行结果为: • ps=7
【例9.4】设计一个函数,通过调用函数read_modem ( ),从调制解调器端口读入一个字符,并将奇偶校验位置成0。 • 字节的位8是奇偶位,将该字节与一个位1到位7为1、位8为0的字节进行与操作,可将该字节的奇偶校验位置成0。表达式ch&127正是将ch中每一位同127数字的对应位进行与操作,结果c h的位8被置成了0。 • /*源程序名:CH0904.C*/ • /*01*/ char get_char_from_modem() • /*02*/ { • /*03*/ char ch;
/*04*/ ch=read_modem(); /*从调制解调器端口中得到一个字符* / • /*05*/ return(ch&127) ; /*可将ch的奇偶校验位置成0*/ • /*06*/ }
【例9.5】位操作符经常用在加密程序中,可以在文件上做一些位操作,从而对原文件进行编码,生成一个不可读磁盘文件。最简单的方法是通过反码运算,将每个字节的每一位取反。设计一个函数encode ( )对字符进行编码。 • /*源程序名:CH0905.C*/ • /*01*/ char encode(ch) • /*02*/ { • /*03*/ char ch; • /*04*/ return (~ch);/*反码运算,对字符进行编码*/ • /*05*/ }
习题二 • 一、选择题 • 1. 在位运算中,操作数每左移一位,则结果相当于________。 • A.操作数乘以2 B.操作数除以2 • C.操作数除以4 D.操作数乘以4 • 2. 若有运算符<<,sizeof,^,&=,则它们按优先级由高至低的正确排列次序是________。 • A.sizeof,&=,<<,^ B.sizeof,<<,^,&= • C.^,<<,sizeof,&= D.<<,^,&=,sizeof • 3. 在c语言中,要求运算数必须是整型或字符型的运算符是________。 • A.&& B.& C.! D.||
4. 以下叙述中不正确的是________。 • A.表达式a&=b等价于a=a&b B.表达式a|=b等价于a=a|b • C.表达式a!=b等价于a=a!b D.表达式a^=b等价于a=a^b
二、填空题 • 1. 在c语言中,&运算符作为单目运算符时表示的是________运算;作为双目运算符时表示的是________运算 • 2. 与表达式 a&=b 等价的另一书写形式是________。 • 3. 与表达式x^=y-2等价的另一书写形式是________。 • 4. 若x=2,y=3,则x&y的结果是________。 • 5. 设a,b为整型量,且a=7,b=8,则表达式 a=a | b<<2 && ~b的值为________。 • 6. 设二进制数a是00101101,若想通过异或运算a^ b使a的高4位取反,低4位不变,则二进制数b应是________。
三、程序分析题(分析程序,写出运行结果) • 1. 下面程序段的运行结果是:________ • unsigned a=0356,b; • b=~a|a<<2+1; • printf(“%x\n”,b); • 2. 请读程序片段: • inta=1,b=2; • if(a&b)printf("***\n"); • elseprintf("$$$\n"); • 以上程序片段的输出结果是:________ • 3. • #include "stdio.h" • void main ( )
{ char x=040 ; printf ( “%d\n”, x=x << 1 ) ; • } • 4. • #include "stdio.h" • void main() • { • unsigned a,b,c,d; • printf("\ninput a octal number(a):"); • scanf(“%o”,&a); /* 输入一个8进制数据 */ • b=( a>>4); /* 将变量a右移4位 */
c=~(~0<<4); /* 设置一个低4位全为1, 其余全为0的数 */ • d=b&c; • printf("a=%o\n%o\n",a,d); • } • 5. • #include "stdio.h" • void main() • { • int i; • unsigned int v; • v=~0; /* 将int 型单元各二进制位置为1 */
for (i=1; (v=v>>1)>0; i++) ; /* 计算int 单元中的位数 */ • printf("\nThe length of INT is:%d", ( i )); • }
四、程序设计题 • 1. 取一个整数a从右端开始的4~7位。 • 2. 输出一个整数中由8~11位构成的数。 • 3. 从键盘上输入1个正整数给int变量num,按二进制位输出该数。
C语言程序设计 主讲:沈济南 TEL:13971887071 E-mail:shenjinan@163.com