本章导读本章是初学 C语言者的一大难点,属较高要求,适合于编写系统软件的需要。读者应在掌握了计算机的几种基本数值编码的基础上,开始本章的学习。通过本章的学习我们将近一步体会到 C语言既具有高级语言的特点,又具有低级语言的功能,它能直接对计算机的硬件进行操作,因而它具有广泛的用途和很强的生命力。
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本书目录第 8章 位运算本章主要知识点
(1) 位运算符的含义及使用
(2) 位运算的特殊应用
(3) 位复合赋值运算符的含义及使用
(4) 位段的定义、位段变量的说明及引用第 8章 位运算
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本章导读
8.1 位运算的 C程序实例
8.2 二进制位运算
8.3 位段
8.4 综合实训
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本章目录
8.1 位运算的 C程序实例
【 例 8.1】
从结果可以看出,在这个程序中出现的几种运算符显然不同于以前,它们不是两个十进制数值之间简单的运算,而是十进制数对应的 二进制“位”的运算 。
【 例 8.1】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本节目录程序演示
【 例 8.1】 编写程序完成对两个整型量进行按位逻辑运算。
程序名为 l8_1.cpp。
#include "stdio.h"
main()
{ int a,b;
printf("input the numbers,");
scanf("%d,%d",&a,&b);
printf("%d\n",a&b); /*输出按位与结果 */
printf("%d\n",a|b); /*输出按位或结果 */
printf("%d\n",a^b); /*输出按位异或结果 */
printf("%u\n",~a); /*输出 a的按位取反结果 */
}
运行情况为:
input the numbers,输入,9,5 < CR >
1
13
12
65526
[例 8.1]程序演示输入源程序弹出运行结果窗口返回例题 返回本节目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本章目录
8.2 二进制位运算
8.2.1 二进制位运算
8.2.2 位复合赋值运算符所谓位运算是指进行 二进制位 的运算。在系统软件中,常要处理二进制位的问题。 C语言提供了按位运算的功能,这使得它与其它高级语言相比,具有很强的优越性。
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

8.2.1 二进制位运算类 型 运算符 含义位逻辑运算符
& 按位与
| 按位或
^ 按位异或
~ 取反移位运算 符
<< 左移
>> 右移
C语言提供了六种位运算符(见表 8-1):
表 8-1位逻辑运算与移位运算返回本节目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

8.2.1 二进制位运算说明:
①运算量只能是 整型或字符型 的数据,不能为实型或结构体等类型的数据。
②六个位运算符的优先级由 高到低 依次为:取反、左移和右移、按位与、
按位异或、按位或。
③两个不同长度的数据进行位运算时,系统会将二者按右端对齐。下面对各种位运算符介绍如下:
返回本节目录
1.“按位与”运算符( &)
2.“按位或”运算符( |)
3.“按位异或”运算符( ^)
4.“求反”运算符 (~ )
5.“左移”运算符 (<<)
6.“右移”运算符 (>>)
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

1.“按位与”运算符( &)
( 1)规则:
参与运算的两数(以补码方式出现)各 对应的二进位相与 (即逻辑乘),
只有对应的两个二进位均为 1时,结果位才为 1,否则为 0,它是双目运算符。
即,0&0=0; 0&1=0; 1&0=0; 1&1=1。
例如:
9&5可写算式如下:
9的二进制补码,0 0 0 0 1 0 0 1
5的二进制补码,0 0 0 0 0 1 0 1
& ___________________
0 0 0 0 0 0 0 1(1的二进制补码 )
可见 9&5=1。
返回 8.2.1目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

1.“按位与”运算符( &)
( 2)特殊用途:
①清零按位与运算通常用来对某些位清 0。由按位与的规则可知:为了使某数的指定位清零,可将该数按位与一特定数。该数中为 1的位,特定数中相应位应为 0;该数中为 0的位,特定数中相应位可以为 0也可以为 1。由此可见,能对某一个数的指定位清零的数并不唯一。 【 例 8.2】
② 取一个数中某些位可将该数与一个特定数进行 &运算,对于要取的那些位,特定数中相应的位设为 1。 【 例 8.3】
③ 取出数中某一位要相将一个数的某一位保留下来,可将该数与一个特定数进行 &运算,特定数的相对应的那位应为 1。 【 例 8.4】
返回本节目录
【 例 8.2】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回 1目录程序演示
【 例 8.2】 对原数 00110110中为 1的位清零。
原数补码,0 0 1 1 0 1 1 0
清零的数,1 1 0 0 0 0 0 0 (或 01000000,00000000等)
& ___________________
0 0 0 0 0 0 0 0
程序可写为:
#include "stdio.h"
main()
{ int a=0x36,b=0xc0,c;
c=a&b;
printf("a=%x\nb=%x\nc=%x\n",a,b,c);
}
运行情况为:
a=36
b=c0
c=0
[例 8.2]程序演示输入源程序弹出运行结果窗口返回例题 返回 1目录
【 例 8.3】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回 1目录程序演示
【 例 8.3】 把 a的高八位清 0,保留低八位。
可作 a&255运算 (255的二进制数为 0000000011111111)。
#include "stdio.h"
main()
{ int a,b=255,c;
scanf("%d",&a);
c=a&b;
printf("a=%x\nb=%x\nc=%x\n",a,b,c);
}
运行情况为:
输入,920< CR >
a=398
b=ff
c=98
[例 8.3]程序演示输入源程序弹出运行结果窗口返回例题 返回 1目录
【 例 8.4】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回 1目录程序演示
【 例 8.4】 编写程序将 a( =9)的最低位取出。程序名为
l8_4cpp。
#include "stdio.h"
main()
{ int a=9,b=1,c;
c=a&b;
printf("a=%x\nb=%x\nc=%x\n",a,b,c);
}
运行情况为:
a=9
b=1
c=1
[例 8.4]程序演示输入源程序弹出运行结果窗口返回例题 返回 1目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

2.“按位或”运算符( |)
( 1)规则参与运算的两数(以补码出现)各 对应的二进位相或 (即逻辑加)。只要对应的两个二进位有一个为 1时,结果位就为 1,它是双目运算符。即,0|0=0; 0|1=1;
1|0=1; 1|1=1。
例如,9|5可写算式如下,0 0 0 0 1 0 0 1
0 0 0 0 0 1 0 1
| _________________
0 0 0 0 1 1 0 1 (十进制为 13)
可见 9|5=13。
( 2)特殊用途将一个数据的某些指定的位置为 1。
将该数按位或一个特定的数,该特定的数的相应位置为 1。 【 例 8.5】
返回 8.2.1目录
【 例 8.5】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回 2目录
【 例 8.5】 将一个数的低 5位置为 1。 程序名为 l8_5.cpp。
只需将该数与,00011111”进行 &运算 。 如:
# # # # # # # # (#可代表 0或 1)
0 0 0 1 1 1 1 1
| _______________
# # # 1 1 1 1 1
#include "stdio.h"
main()
{ int a,b=31,c;
scanf("%d",&a);
c=a|b;
printf("a=%x\nb=%x\nc=%x\n",a,b,c);
}
运行情况为:
输入,5< CR >
a=5
b=1f
c=1f
程序演示
[例 8.5]程序演示输入源程序弹出运行结果窗口返回例题 返回 2目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

3.“按位异或”运算符( ^)
( 1)规则参与运算的两数(以补码出现)各 对应的二进位相异或,当两对应的二进位相异时,
结果为 1,它是双目运算符。即,0^0=0; 0^1=1; 1^0=1; 1^1=0。
例如,9^5可写成算式如下,0 0 0 0 1 0 0 1
0 0 0 0 0 1 0 1
^_____________
0 0 0 0 1 1 0 0 (十进制为 12) 可见 9^5=12。
( 2)特殊用途
①使特定位翻转 【 例 8.6】
要使哪几位翻转就将与其进行“按位异或”运算的数的相应位置为 1。
②使特定位保留原值要使哪几位保留原值就将与其进行“按位异或”运算的数的相应位置为 0。
③交换两个值,不用临时变量 【 例 8.7】
返回 8.2.1目录
【 例 8.6】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回 3目录
【 例 8.6】 将 01110001的低 4 位翻转,高 4位保留原值 。
0 1 1 1 0 0 0 1
0 0 0 0 1 1 1 1
^ ______________
0 1 1 1 1 1 1 0 (十进制 126)
程序可写为:
#include "stdio.h"
main()
{ int a=0x71,b=0xf,c;
c=a^b;
printf("a=%x\nb=%x\nc=%x\n",a,b,c);
}
运行情况为:
a=71
b=f
c=7e
程序演示
[例 8.6]程序演示输入源程序弹出运行结果窗口返回例题 返回 3目录
【 例 8.7】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

【 例 8.7】 设有整型数 a=5,b=7。 编写程序利用位运算,将 a和 b的值互换 。
程序中,通过顺序使用 a=a^b;b=b^a;a=a^b;三个赋值语句将两变量 a、
b的值互换 。 具体计算过程如下:
第一步,a 0000000000000101
b 0000000000000111
a=a^b 0000000000000010
第二步,a 0000000000000010
b 0000000000000111
b=b^a 0000000000000101 (b的值为 5)
第三步,a 0000000000000010
b 0000000000000101
a=a^b 0000000000000111 ( a的值为 7)
返回 3目录
【 例 8.7】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

程序名为 l8_7.cpp。
#include "stdio.h"
main()
{ int a=5,b=7;
printf("a=%d,b=%d\n",a,b);
a=a^b;b=b^a;a=a^b;
printf("a=%d,b=%d\n",a,b);
}
运行情况为:
a=5,b=7
a=7,b=5
程序演示返回 3目录
[例 8.7]程序演示输入源程序弹出运行结果窗口返回例题 返回 3目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

4.“求反”运算符 (~)
( 1)规则对参与运算的数的 各二进位按位求反,它是单目运算符,具有右结合性。
即,~0=1; ~1=0。
例如:~ 9的运算为
0000000000001001
~ __________________
1111111111110110
( 2)用途适当的使用可增加程序的移植性。如要将整数 a的最低位置为 0,我们通常采用语句 a=a&~1;来完成,因为这样对 a是 16位数还是 32位数均不受影响。
返回 8.2.1目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

5.“左移”运算符 (<<)
( 1)规则把,<<”左边的运算数的 各二进位全部左移若干位,由,<<”右边的数指定移动的位数,高位丢弃,低位补 0,它是双目运算符。
例如,a<<4指把 a的各二进位向左移动 4位。如 a=00000011(十进制 3),左移
4位后为 00110000(十进制 48)。
( 2)特殊用途左移 1位相当于该数乘以 2;左移 n位相当于该数乘以 2n 。但此结论只适用于该数左移时被溢出舍弃的高位中不包含 1的情况。
左移比乘法运算快得多,有的 C编译系统自动将乘 2运算用左移一位来实现。
返回 8.2.1目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

6.“右移”运算符 (>>)
( 1)规则把,>>”左边的运算数的 各二进位全部右移若干位,,>>”右边的数指定移动的位数。
( 2)特殊用途右移 1位相当于该数除以 2;右移 n位相当于该数除以 2n。
( 3)说明对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补 0;而为负数时,符号位为 1,最高位是补 0还是补 1取决于计算机系统的规定。移入 0的称为
“逻辑右移”;移入 1的称为“算术右移”。我们可以通过编写程序来验正所使用的系统是采用“逻辑右移”还是“算术右移”。很多系统规定为补 1,即“算术右移”。
如,a,1001011111101101
a>>1,0100101111110110 ( 逻辑右移 )
a>>1,1100101111110110 ( 算术右移 )
返回 8.2.1目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本节目录
8.2.2 位复合赋值运算符位运算符与赋值运算符结合组成位复合赋值运算符。位复合赋值运算符与算术复合赋值运算符相似,它们的运算级别较低,仅高于逗号运算符,
是自右而左的结合性。
1.分类位复合赋值运算符如 表 8-2所示:
2.运算过程
( 1)先对两个操作数进行位操作。
( 2)再将结果赋予第一个操作数(因此第一个操作数必须是变量)。如:
a&=2;表示 a=a&2;。
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回 8.2.2目录表 8-2 位复合赋值运算符运算符 名称 例子 等价于
&= 位与赋值 a&=b a=a&b
|= 位或赋值 a|=b a=a|b
^= 位异或赋值 a^=b a=a^b
>>= 右移赋值 a>>=b a=a>>b
<<= 左移赋值 a<<=b a=a<<b
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本章目录
8.3 位段
1.位段的定义与位段中数据是引用
2.对于位段使用的几点说明
3.应用举例 【 例 8.8】
为了节省存储空间,并使处理简便,C语言提供了又一种使用结构的方法 —— 位段(也称位域)。所谓位段就是以位为单位定义长度的结构体类型中的成员。每个位段都有一个位段名,
允许在程序中引用位段中的数据进行操作。 这样就可以将若干个信息紧缩存放,仅用一个或几个字节来表示。
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本节目录
1.位段的定义与位段中数据是引用
( 1)位段的定义
( 2)位段中数据的引用
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回 1目录
(1)位段的定义位段的定义就是将它定义为结构体中的成员,其形式为:
struct <结构体名 >
{ <位段表列 >
};
struct <结构体名 > <结构变量表列 >;
或 struct <结构体名 >
{ <位段表列 >
}<结构变量表列 >;
或 struct
{ <位段表列 >
}<结构变量表列 >;
在这三种形式中,位段表列的形式均为:
<类型说明符 > <位段名 >,<位段长度 >;
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

(1)位段的定义例如,struct bs
{ unsigned a:8;
unsigned b:2;
unsigned c:6;
}data;
表示 data为 struct bs型结构变量(也称位段变量),共占两个字节。其中位段 a占 8位,位段 b占 2位,位段 c占 6位,如图 8-1所示。
8 2 6 (位)
图 8-1data位段变量内存示意图
a b c
返回 1目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

( 2)位段中数据的引用位段在本质上就是一种结构体类型中的成员,只不过是按二进制位分配的。因此,位段中数据的引用可按结构变量成员的访问方法进行,形式为:
<结构变量 >.<位段名 >
如,data.a=2;表示给位段变量 data的位段 a赋值为 2。
返回 1目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

2.对于位段使用的几点说明
( 1)一个位段必须存储在同一个存储单元中,不能跨跃两个单元。
如一个单元所剩空间不够存放下一位段时,应从下一单元起存放该位段。
( 2)可以有意使某位段从下一单元开始存放。
( 3)位段中的数据不能超过其允许的最大值范围,位段的长度不能大于机器字长。例如,定义位段的长度为 2位,则其中存放的最大数为 3。
( 4)位段可以无名位段,无名位段是不能使用的,它只用来作填充或调整位置。
( 5)不能定义位段数组,也不能定义返回值为位段的函数。
( 6)位段允许在数据表达式中引用,它会被系统自动地转换为整型数。
返回本节目录
【 例 8.8】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

【 例 8.8】 位段变量和指向位段的指针变量的使用 。 程序名为 l8_8.cpp。
#include "stdio.h"
main()
{ struct bs
{ unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1; /*分别给三个位段赋值 */
bit.b=7;
bit.c=15;
printf("%d,%d,%d\n",bit.a,bit.b,bit.c);/*以整型量格式输出三个位段的内容 */
pbit=&bit; /*把位段变量 bit的地址送给指针变量 pbit*/
pbit->a=0; /*用指针方式给位段 a重新赋值,赋为 0*/
返回本节目录
【 例 8.8】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

pbit->b&=3;
pbit->c|=1;
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);
/*用指针方式输出了这三个位段的值 */
}
运行结果为:
1,7,15
0,3,15
程序演示返回本节目录
[例 8.8]程序演示输入源程序弹出运行结果窗口返回例题 返回本节目录
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

返回本章目录
8.4 综合实训
[例 8.9]
[例 8.10]
【 例 8.9】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

【 例 8.9】 取 一个数 a从右端开始的第 5至 8位 。 程序名为 l8_9.cpp。
分析:
( 1) 先使 a右移 5位,见图 8-4,目的是使要取出的那几位移到最右端 。
( 2) 设置一个低 4位 (即 8-5+1)全为 1,其余的位全为 0的数,即将一个全 1的数左移 4位,这样右端低 4位为 0。
( 3) 将上面两数按位与,将低 4位保留 。
返回本节目录
15 9 8 5 4 3 0
15 9 8 5 4 3 0
当然,对一个数可以任意指定从其右面第 m位开始向右取 n位,只需将程序中的,a>>5”改成,a>>m”,且将,~(~0<<3)”改成,~(~0<<(m-n+1))”即可 。
【 例 8.9】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

#include "stdio.h"
main()
{ unsigned a,b,c,d;
printf("input a number:");
scanf("%d",&a);
b=a>>5;
c=~(~0<<4);
d=b&c;
printf("a=%x\td=%x\n",a,d);
}
运行情况如下:
input a number,输入,421 < CR >
a=01a5 d=d
返回本节目录程序演示
[例 8.9]程序演示输入源程序弹出运行结果窗口返回例题 返回本节目录
【 例 8.10】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

【 例 8.10】 对某个整数 x进行左循环移位,移 n位 。 程序名为 l8_10.cpp。
分析:设该数用 2个字节存放,则原数中右边的 16-n位向左移动,原数中左边的 n位移至右边,如图 8-5所示 。 ① 先把 x赋给中间变量 a,b。
② 把 a右移 16-n位,使原数中左边的 n位移至右边,其余各位为 0。
③ 再把 b左移 n位,使原数中右边的 16-n位移至最左边,其余各位为 0。
④ 上述两数按位相或 。
返回本节目录
n 16-n
16-n n
【 例 8.10】 实例
《C
语言程序设计
,(V
isu
al
C+
+
6.
0
环境

#include "stdio.h"
main()
{ unsigned x,a,b,c;
int n;
scanf("%d,%d",&x,&n);
a=x>>(16-n);
b=x<<n;
c=a|b;
printf("x=%x,c=%x",x,c);
}
运行情况如下:
输入,400,3 < CR >
x=190,c=c80
返回本节目录程序演示
[例 8.10]程序演示输入源程序弹出运行结果窗口返回例题 返回本节目录