C语言程序设计教程郧阳师范高等专科学校计算机科学系方 斌 制作
§ 12.1 概述 § 12.4 位段
§ 12.2 位运算 本章要求及作业
§ 12.3 位运算举例第 12章 位运算
11.1 概述位运算,是指进行二进制位的运算。
一、字节与位位( Bit),二进制位是计算机中最小的信息单位。
一位二进制能表达两个信息,0”和,1”。
2位二进制能表达 4个信息,00 01 10 11
2位二进制能表达 8个信息,000 001 010 011 100 101 110 111
8位二进制能表达 256个信息,0000,0000,.....,1111,1111
n位二进制能表达 个信息。
为什么用二进制位作为计算机中最小的信息单位?
电子计算机的主要部件( CPU、存储器等)是由成千上万的数字电路组成的,每一个电路有两种稳定的工作状态,可以方便地表示一位二进制的,0”和,1”。
一般用字节( Byte)作为计算机信息的基本单位,一个字节由 8个二进制位组成,其中最右边的一位称为“最低有效位” LSB,最左面的一位称为“最高有效位” MSB。
一般用 1字节,2字节,4字节,8字节表示一个信息。例如,用 1字节表示一个英文字符,2字节表示一个汉字字符,4字节表示一个实数,....等。
某些信息可以用一位二进制表达,如:
打印机状态:
bit0,打印机在规定的时间内未完成上一次指定的操作。
bit5,打印机当前缺纸。
bit7,打印机当前准备好。
二、数的表达方式可以用 1字节,2字节,4字节或 8字节表示一个数。以下用 1字节的情况为例。
1、无符号数无符号数只有值,没有符号,1字节无符号数表达的数的范围:
0000,0000~ 1111,1111,即 0~ 255。
2、有符号数如何表示符号?如何表达值?
以 1字节表示有符号数,最高位 bit7表示符号,bit6~ bit0表示值。
以 2字节表示有符号数,最高位 bit15表示符号,bit14~ bit0表示值。
符号的表示方法,0---正,1---负。
值的表示方法:原码、反码、补码。一般用补码。
( 1)原码。原码表示的值是数的绝对值。例、
+7的原码,00000111
-7的原码,10000111
( 2)反码。
正数的反码和其原码相同。
负数的反码是:符号位为 1,数值位是对该数的原码的相应位取反。
如:
+7的反码,00000111
-7的反码,11111000
+0的反码,00000000
-0的反码,11111111
( 3)补码。
原码和补码都不便于计算机的运算,因为在运算中要单独处理其符号。要将符号位与其它位一样处理就用补码表示数据。
A、“模”的概念
“模”是指一个计量系统的计数范围。如时钟等。计算机也可以看成一个计量机器,它也有一个计量范围,即都存在一个“模”。例:
时钟的计量范围是 0~ 11,模 =12。
表示 n位的计算机计量范围是 0~ -1,模 = 。
模实质上是计量器产生“益出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算。
例:设当前时针指向 10点,而准确时间是 6点,调整时间可有以下两种拨法:
一种是倒拨 4小时,即,10-4=6
另一种是顺拨 8小时,10+8=12+6=6
在以 12模的系统中,加 8和减 4效果是一样的,因此凡是减 4运算,都可以用加 8来代替。
对模而言,8和 4互为 补数 。实际上以 12模的系统中,11和 1,10和 2,9和
3,7和 5,6和 6都有这个质。共同的特点是两者相加等于模。
对于计算机,其概念和方法完全一样。 n位计算机,设 n=8,所能表示的最大数是 11111111,若再加 1称为 100000000(9位 ),但因只有 8位,最高位 1自然丢失。又回了 00000000,所以 8位二进制系统的模为 。 在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。
把补数用到计算机对数的处理上,就是补码。
B、补码的定义对于 n位计算机,某数 x的补码是:
正数的补码:与原码相同,
负数的补码:符号位为 1,数值位是对该数的原码的相应位取反,
然后整个数再加 1。
例如,+7的补码,00000111
-7的补码:写出原码 10000111
数值位取反 11111000
加 1 11111001
即 -7的补码是 11111001
计算机是以补码形式存放数的。
3、实数实数有符号、整数部分、小数部分和小数点。在十进制中,实数的一般表达方式是:
0<s<10
在二进制中,实数可以表达为:
J称为阶码(整数),2为阶码的底,0<S<2。
32位实数的表达方式为:
三,符号的表达方式要让计算机处理各种符号,如,汉字、英文字母、标点符号、数字、数学符号、物理符号等,也必须对这些符号进行编码。人类使用的符号非常多,只能选出常用部分字符进行编码,供计算机处理,被选出的供计算机处理的符号称为字符集。
1,ASCII码英文字符集是美国信息交流标准代码,简称 ASCII码( American
Standard Code for Information Interchange)。
ASCII码采用 7位二进制编码,共计 128个字符,包含:
26个大写英文字符,A~Z。( A=41H,B=42H,….)
26个小写英文字符,a~z。( a=61H,b=62H,….)
10个数字,0~9。( 0=30H,1=31H,….,9=39H)
其他字符。 (空格键 SP=20H,ESC键 =1AH,回车 CR=0DH,换行
LF=0AH)。
2,汉字编码
( 1) GB2312-80。
简体中文国标字库( 1981年订,中国大陆)。 7445个字符,其中汉字 6773个,包括一级汉字 3755个,二级汉字 3008个。采用 2字节( 16位二进制)编码。
区位码:国标 GB2312规定,所有的国标汉字与符号组成一个 94× 94的矩阵。在此方阵中,每一行称为一个 "区 ",每一列称为一个 "位 ",因此,这个方阵实际上组成了一个有
94个区 (区号分别为 0 1到 94)、每个区内有 94个位 (位号分别为 01到 94)的汉字字符集。一个汉字所在的区号和位号简单地组合在一起就构成了该汉字的 "区位码 "。在汉字的区位码中,高两位为区号,低两位为位号。由此可见,区位码与汉字或符号之间是一一对应的。
内码:汉字的内码是指在计算机中表示汉字的编码。机内码与区位码稍有区别。为什么不直接用区位码作为计算机内的编码呢? 这是因为汉字的区码和位码的范围都在 1
到 94内,如果直接用区位码作机内码,就会与基本 ASCII码冲突。 汉字的内码通常与所使用的计算机系统有关。目前,对于国内大多数的计算机系统,一个汉字的内码占两个字节,分别称为高位字节与低位字节,且这两位字节与区位码的关系如下,内码高位
=区码 +A0H(H表示十六进制 ) 内码低位 =位码 +A0H 例如,汉字 "啊 "的区位码为
"1601",区码和位码分别用十六进制表示即为 "1001H",则它的内码为 "B0A1H"。其中 B0H为内码的高位字节,A1H为内码的低位字节。
( 2) BIG5码。
香港、台湾地区使用的汉字编码。 13053个汉字。
( 3) HZ码。
便于 Internet传输的 7位汉字编码字符集。(某些 Internet主机不支持 8位码的传输)。
( 4) ISO10646( 1992年)。国际标准大字符集汉字系统( CJK:中、
日、韩统一汉字编码字符集),20902个汉字。通用多 8位编码( UCS)。
对应的中国国家标准是 GB-13000,其内码标准为 GBK国家汉字扩充内码规范( 1996年)。
注意:某些软件可能只能识别一种或几种汉字编码,当遇到不能识别的汉字编码时,显示出乱字符(称 "乱码 "),此时,应使用内码转换软件,如 "
两岸通 ","南极星 "等。
四,电压的编码表示计算机要处理电压、电流、温度、压力、位移、速度等模拟量信息时,也必须把这些信息进行编码。把模拟量用数字表示称为 "模 /数转换 "( A/D转换)。模数转换一般用专门的硬件电路来完成,这种硬件电路称为 A/D转换器( ADC,A/D
Converter)。
一般 A/D转换器能够把电压转换为与之成正比的数字。例如,用 16位二进制数表示
-5V~+5V电压,则 -5V=-32768,+5V=+32767。
对于其他类型的模拟量,一般使用称为 "传感器 "的设备将其转换为电压,再由 A/D
转换器变换为计算机能够处理的数字。
计算机进行计算后,可能需要控制生产设备,如电机、阀门等,而这些设备需要电压等模拟信号才能控制,因此,需要把计算机运算得到的数字变换为电压等模拟量,
这一过程称为 "数 /模转换 "( D/A转换),一般由称为 "D/A转换器( DAC) "的硬件电路来完成。
五,声音、图象的编码表示在计算机中,声音、图象也用编码表示,编码一般存为一个文件,如:
WAV,MIDI,MP3声音文件。
BMP,JPG,GIF:图形文件。
AVI,MOV:电影文件为了节省内存空间,在系统软件中常将多个标志状态简单地组合在一起,
存储到一个字节(或字)中。C语言是为研制系统软件而设计的,所以她提供了实现将标志状态从标志字节中分离出来的位运算功能。
所谓位运算是指,按二进制位进行的运算。
12.2 位运算
C语言提供的位运算符:
& 按位与 | 按位或
∧ 按位异或 ~ 取反
<< 左移 >> 右移说明:
1,位运算符中除了 ~以外,均为二目运算符。
2、运算量只能是整型或字符型的数据,不能为实型数据。
一、“按位与”运算符:
规则:
参加运算的两个运算量,如果两个相应位都为 1,则该位结果值为 1,
否则为 0。
例如,X=10001001(137) Y=11101110 (238)
因此,X & Y=10001000 (136)
按位与的特殊用途:
1、清零。
方法,与一个各位都为零的数值相与,结果为零。
2、取一个数 x中某些指定位。
方法,找一个数,此数的各位是这样取值的:对应 x数要取各位,该数对应位为 1,其余位为零。此数与 x相就可以得到 x中的某些位。
例:设 X=10101110
(1)取 X的低 4位
(2)取 X的 bit2,bit4,bit6位二、“按位或”运算符规则:
参加运算的两个运算量,如果两个相应位中有个为 1,则该位结果值为 1,
否则为 0。
例如 X=10001001(137) Y=11101110(238)
X | Y=11101111(239)
按位或的特殊用途,常用来对一个数据的某些位置 1。
方法:找一个数,此数的各位是这样取值的,对应 x数要置 1的位,该数对应位为 1,其余位为零。此数与 x相或就可使 x中的某些位置 1。
例:使 x=10100000 的低 4位为 1。
三、“异或”运算符规则:参加运算的两个运算量,如果两个相应位为“异” (值不同),
则该位结果值为 1,否则为 0。
例如 X=10001001(137) Y=11101110(238)
X^Y=01100111(103)
异或运算的应用:
( 1)使特定位翻转,找一个数,此数的各位是这样取值的:对应 x数要转的各位,该数对应位为 1,其余位为零。此数与 x相异或即可。
例,x=10101110,使 x低 4位翻转
(2)与 0相异或,保留原值。例、
四、“取反”运算符 ~ (单目运算符 )
对一个二进制数按位取反,即将 0变为 1,1变为 0。
例,0100001110010111 取反 1011110001101000
注意,~运算符的优先级别比算术运算符、关系运算符、逻辑运符和其它运算符都高。
例:使一个数 a的最低位为零,可以表示成:
a & ~ 1
因为,~1=1111111111111110。
五、左移运算符 <<
将一个数的各二进制全部左移若干位。(左丢弃,右补 0)
例,a=a<<2 将 a的二进制数左移 2位,右补 0。
若左移时舍弃的高位不包含 1,则数每左移一位,相当该数乘以 2。
六、右移运算符 >>
将一个数的各二进制位全部右移若干位。(正数左补 0,z负数左补 1,右丢弃)
例,a=a>>2 将 a的二进制数右移 2位。
将一操作数左移一位,相当于将其乘 2。将一操作数右移一位,相当于将其除以 2。
因此,可以用移位操作代替部分乘除操作,只要不产生溢出,这种代替是正确的。
(用 CF标志判别无符号数运算是否溢出,CF=1,表示溢出。
用 OF标志判别有符号数运算是否溢出,OF=1,表示溢出)。
七、位运算符与赋值运算符结合,组成展的赋值运算符。
&=,例,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
12.3 位运算举例例 [12.1] 取一个整数 a从右端开始的 4~ 7位。 比如,
0000,0000,1101,1001
(八进制 331/十进制 217,4~ 7位 1101的八进制值是 15/十进制 13)
方法:
( 1)先使 a右移 4位,使要取出的几位移到最右端。 a>>4
( 2)设置一个低 4位全为 1,其余为 0 的数 ~( ~0<<4)
( 3)将上面两者进行 &运算。
程序:
main()
{
unsigned a,b,c,d;
scanf(“%o”,&a);
b = a >> 4;
c = ~(~0 << 4); /* 0x000f */
d = b & c;
printf(“%o\n%o\n”,a,b);
}
例 [12.2] 将 a进行循环右移,如原来右端 3位移到最左端 3位。
1101111110101011 --> 0111101111110101
注意,不能直接使用 >>运算符,因为,>>运算符使左面添 0或添 1。
算法:设需要循环右移的数是一个整数 (两字节 )。移动 n位
( 1)将 a的右端 n位先放到中间变量 b的高 n位中。 (左移 16-n位 )
b= a<<(16-n);
( 2)将 a右移 n位,其左面 n位补 0。
c=a>>n;
( 3)将 c与 b 进行按位或运算。
c=a | b
程序:
void main()
{
unsigned a,b,c;
int n;
scanf("a=%o,n=%d",&a,&n);
b=a<<(16-n);
c=a>>n;
c=c|b;
printf(“%o\n%o”,a,c);
}
12.4 位段信息可以用用 1字节,2字节,4字节,8字节表示。例如,用 1字节表示一个英文字符,2字节表示一个汉字字符,4字节表示一个实数,....等。
某些信息可以用一位二进制表达,如:
打印机状态:
bit0,打印机在规定的时间内未完成上一次指定的操作。
bit5,打印机当前缺纸。
bit7,打印机当前准备好。
位段的概念与定义所谓位段类型,是一种特殊的结构类型,其所有成员均以二进制位为单位定义长度,并称成员为位段。
例如,CPU的状态寄存器,按位段类型定义如下:
struct status
{ unsigned sign:1; /*符号标志 */
unsigned zero:1; /*零标志 */
unsigned carry:1; /*进位标志 */
unsigned parity:1; /*奇偶 /溢出标志 */
unsigned half_carry:1; /*半进位标志 */
unsigned negative:1; /*减标志 */
} flags;
显然,对 CPU的状态寄存器而言,使用位段类型(仅需 1个字节),比使用结构类型(需要 6个字节)节省了 5个字节。
某些信息也可以用几位二进制表达。例如,数学协处理器的控制寄存器,有如下结构:
BIT5~ BIT0:异常屏蔽位。置 0时把相应的异常传送给 CPU的异常处理程序,
置 1时把相应的异常传送给协处理器的异常处理程序。
PC,Precision Control,精度控制,指定有效数字的位数(符号位加尾数):
00,24位(短实数)
01:保留
10,53位(长实数)
11,64位(临时实数)
RC,Rounding Control,舍入控制
00,舍入到最接近的值(缺省)
01:舍入到负无穷
10:舍入到正无穷
11:截断在C语言中,为了方便访问这些信息,定义了“位段”
类型,它是用结构体来表示的。
例 1、
struct FPU
{
unsigned,4; /* 无名位段,占 4位 */
unsigned RC:2 /* RC位段,占 2位 */
unsigned PC:2;
unsigned,2; /* 无名位段,占 2位 */
unsigned PM:1; /* PM位段,占 1位 */
unsigned UM:1;
unsigned OM:1;
unsigned ZM:1;
unsigned DM:1;
unsigned IM:1;
};
例2、
struct packed_data
{
unsigned a:2;
unsigned b:6;
unsigned c:4;
unsigned d:4;
int i;
} data
例3、各个位段不恰好占一个字节
struct packed_data
{
unsigned a:2;
unsigned b:3;
unsigned c:4;
int i;
} data
位段的引用:
如,在例3中,可以给位段赋值,
data.a = 2; /* 注意,位段 a占 2位,允许值的范围是 0~ 3 */
data.b = 7; /* 注意,位段 b占 3位,允许值的范围是 0~ 7 */
data.c = 9; /* 注意,位段 c占 4位,允许值的范围是 0~ 15 */
printf("%d %d %d",data.a,data.b,data.c);
说明
( 1)因为位段类型是一种结构类型,所以位段类型和位段变量的定义,
以及对位段(即位段类型中的成员)的引用,均与结构类型和结构变量一样。
( 2)对位段赋值时,要注意取置范围。一般地说,长度为 n的位段,其取值范围是,0~( 2n-1)。
( 3)使用长度为 0的无名位段,可使其后续位段从下 1个字节开始存储。
例如
struct status
{ unsigned sign:1; /*符号标志 */
unsigned zero:1; /*零标志 */
unsigned carry:1; /*进位标志 */
unsigned,0; /*长度为 0的无名位段 */
unsigned parity:1; /*奇偶 /溢出标志 */
unsigned half_carry:1; /*半进位标志 */
unsigned negative:1; /*减标志 */
} flags;
原本 6个标志位是连续存储在 1个字节中的。由于加入了 1个长度为 0的无名位段,所以其后的 3个位段,从下 1个字节开始存储,一共占用 2个字节。
( 4) 1个位段必须存储在 1个存储单元(通常为 1字节)中,不能跨 2
个。如果本单元不够容纳某位段,则从下 1个单元开始存储该位段。
( 5)可以用 %d,%x,%u和 %o等格式字符,以整数形式输出位段。
( 6)在数值表达式中引用位段时,系统自动将位段转换为整型数。
本章要求及作业
1、位运算及其特殊用途。
2、信息的位段表示。位段数据类型。
作业,12.1
§ 12.1 概述 § 12.4 位段
§ 12.2 位运算 本章要求及作业
§ 12.3 位运算举例第 12章 位运算
11.1 概述位运算,是指进行二进制位的运算。
一、字节与位位( Bit),二进制位是计算机中最小的信息单位。
一位二进制能表达两个信息,0”和,1”。
2位二进制能表达 4个信息,00 01 10 11
2位二进制能表达 8个信息,000 001 010 011 100 101 110 111
8位二进制能表达 256个信息,0000,0000,.....,1111,1111
n位二进制能表达 个信息。
为什么用二进制位作为计算机中最小的信息单位?
电子计算机的主要部件( CPU、存储器等)是由成千上万的数字电路组成的,每一个电路有两种稳定的工作状态,可以方便地表示一位二进制的,0”和,1”。
一般用字节( Byte)作为计算机信息的基本单位,一个字节由 8个二进制位组成,其中最右边的一位称为“最低有效位” LSB,最左面的一位称为“最高有效位” MSB。
一般用 1字节,2字节,4字节,8字节表示一个信息。例如,用 1字节表示一个英文字符,2字节表示一个汉字字符,4字节表示一个实数,....等。
某些信息可以用一位二进制表达,如:
打印机状态:
bit0,打印机在规定的时间内未完成上一次指定的操作。
bit5,打印机当前缺纸。
bit7,打印机当前准备好。
二、数的表达方式可以用 1字节,2字节,4字节或 8字节表示一个数。以下用 1字节的情况为例。
1、无符号数无符号数只有值,没有符号,1字节无符号数表达的数的范围:
0000,0000~ 1111,1111,即 0~ 255。
2、有符号数如何表示符号?如何表达值?
以 1字节表示有符号数,最高位 bit7表示符号,bit6~ bit0表示值。
以 2字节表示有符号数,最高位 bit15表示符号,bit14~ bit0表示值。
符号的表示方法,0---正,1---负。
值的表示方法:原码、反码、补码。一般用补码。
( 1)原码。原码表示的值是数的绝对值。例、
+7的原码,00000111
-7的原码,10000111
( 2)反码。
正数的反码和其原码相同。
负数的反码是:符号位为 1,数值位是对该数的原码的相应位取反。
如:
+7的反码,00000111
-7的反码,11111000
+0的反码,00000000
-0的反码,11111111
( 3)补码。
原码和补码都不便于计算机的运算,因为在运算中要单独处理其符号。要将符号位与其它位一样处理就用补码表示数据。
A、“模”的概念
“模”是指一个计量系统的计数范围。如时钟等。计算机也可以看成一个计量机器,它也有一个计量范围,即都存在一个“模”。例:
时钟的计量范围是 0~ 11,模 =12。
表示 n位的计算机计量范围是 0~ -1,模 = 。
模实质上是计量器产生“益出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算。
例:设当前时针指向 10点,而准确时间是 6点,调整时间可有以下两种拨法:
一种是倒拨 4小时,即,10-4=6
另一种是顺拨 8小时,10+8=12+6=6
在以 12模的系统中,加 8和减 4效果是一样的,因此凡是减 4运算,都可以用加 8来代替。
对模而言,8和 4互为 补数 。实际上以 12模的系统中,11和 1,10和 2,9和
3,7和 5,6和 6都有这个质。共同的特点是两者相加等于模。
对于计算机,其概念和方法完全一样。 n位计算机,设 n=8,所能表示的最大数是 11111111,若再加 1称为 100000000(9位 ),但因只有 8位,最高位 1自然丢失。又回了 00000000,所以 8位二进制系统的模为 。 在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。
把补数用到计算机对数的处理上,就是补码。
B、补码的定义对于 n位计算机,某数 x的补码是:
正数的补码:与原码相同,
负数的补码:符号位为 1,数值位是对该数的原码的相应位取反,
然后整个数再加 1。
例如,+7的补码,00000111
-7的补码:写出原码 10000111
数值位取反 11111000
加 1 11111001
即 -7的补码是 11111001
计算机是以补码形式存放数的。
3、实数实数有符号、整数部分、小数部分和小数点。在十进制中,实数的一般表达方式是:
0<s<10
在二进制中,实数可以表达为:
J称为阶码(整数),2为阶码的底,0<S<2。
32位实数的表达方式为:
三,符号的表达方式要让计算机处理各种符号,如,汉字、英文字母、标点符号、数字、数学符号、物理符号等,也必须对这些符号进行编码。人类使用的符号非常多,只能选出常用部分字符进行编码,供计算机处理,被选出的供计算机处理的符号称为字符集。
1,ASCII码英文字符集是美国信息交流标准代码,简称 ASCII码( American
Standard Code for Information Interchange)。
ASCII码采用 7位二进制编码,共计 128个字符,包含:
26个大写英文字符,A~Z。( A=41H,B=42H,….)
26个小写英文字符,a~z。( a=61H,b=62H,….)
10个数字,0~9。( 0=30H,1=31H,….,9=39H)
其他字符。 (空格键 SP=20H,ESC键 =1AH,回车 CR=0DH,换行
LF=0AH)。
2,汉字编码
( 1) GB2312-80。
简体中文国标字库( 1981年订,中国大陆)。 7445个字符,其中汉字 6773个,包括一级汉字 3755个,二级汉字 3008个。采用 2字节( 16位二进制)编码。
区位码:国标 GB2312规定,所有的国标汉字与符号组成一个 94× 94的矩阵。在此方阵中,每一行称为一个 "区 ",每一列称为一个 "位 ",因此,这个方阵实际上组成了一个有
94个区 (区号分别为 0 1到 94)、每个区内有 94个位 (位号分别为 01到 94)的汉字字符集。一个汉字所在的区号和位号简单地组合在一起就构成了该汉字的 "区位码 "。在汉字的区位码中,高两位为区号,低两位为位号。由此可见,区位码与汉字或符号之间是一一对应的。
内码:汉字的内码是指在计算机中表示汉字的编码。机内码与区位码稍有区别。为什么不直接用区位码作为计算机内的编码呢? 这是因为汉字的区码和位码的范围都在 1
到 94内,如果直接用区位码作机内码,就会与基本 ASCII码冲突。 汉字的内码通常与所使用的计算机系统有关。目前,对于国内大多数的计算机系统,一个汉字的内码占两个字节,分别称为高位字节与低位字节,且这两位字节与区位码的关系如下,内码高位
=区码 +A0H(H表示十六进制 ) 内码低位 =位码 +A0H 例如,汉字 "啊 "的区位码为
"1601",区码和位码分别用十六进制表示即为 "1001H",则它的内码为 "B0A1H"。其中 B0H为内码的高位字节,A1H为内码的低位字节。
( 2) BIG5码。
香港、台湾地区使用的汉字编码。 13053个汉字。
( 3) HZ码。
便于 Internet传输的 7位汉字编码字符集。(某些 Internet主机不支持 8位码的传输)。
( 4) ISO10646( 1992年)。国际标准大字符集汉字系统( CJK:中、
日、韩统一汉字编码字符集),20902个汉字。通用多 8位编码( UCS)。
对应的中国国家标准是 GB-13000,其内码标准为 GBK国家汉字扩充内码规范( 1996年)。
注意:某些软件可能只能识别一种或几种汉字编码,当遇到不能识别的汉字编码时,显示出乱字符(称 "乱码 "),此时,应使用内码转换软件,如 "
两岸通 ","南极星 "等。
四,电压的编码表示计算机要处理电压、电流、温度、压力、位移、速度等模拟量信息时,也必须把这些信息进行编码。把模拟量用数字表示称为 "模 /数转换 "( A/D转换)。模数转换一般用专门的硬件电路来完成,这种硬件电路称为 A/D转换器( ADC,A/D
Converter)。
一般 A/D转换器能够把电压转换为与之成正比的数字。例如,用 16位二进制数表示
-5V~+5V电压,则 -5V=-32768,+5V=+32767。
对于其他类型的模拟量,一般使用称为 "传感器 "的设备将其转换为电压,再由 A/D
转换器变换为计算机能够处理的数字。
计算机进行计算后,可能需要控制生产设备,如电机、阀门等,而这些设备需要电压等模拟信号才能控制,因此,需要把计算机运算得到的数字变换为电压等模拟量,
这一过程称为 "数 /模转换 "( D/A转换),一般由称为 "D/A转换器( DAC) "的硬件电路来完成。
五,声音、图象的编码表示在计算机中,声音、图象也用编码表示,编码一般存为一个文件,如:
WAV,MIDI,MP3声音文件。
BMP,JPG,GIF:图形文件。
AVI,MOV:电影文件为了节省内存空间,在系统软件中常将多个标志状态简单地组合在一起,
存储到一个字节(或字)中。C语言是为研制系统软件而设计的,所以她提供了实现将标志状态从标志字节中分离出来的位运算功能。
所谓位运算是指,按二进制位进行的运算。
12.2 位运算
C语言提供的位运算符:
& 按位与 | 按位或
∧ 按位异或 ~ 取反
<< 左移 >> 右移说明:
1,位运算符中除了 ~以外,均为二目运算符。
2、运算量只能是整型或字符型的数据,不能为实型数据。
一、“按位与”运算符:
规则:
参加运算的两个运算量,如果两个相应位都为 1,则该位结果值为 1,
否则为 0。
例如,X=10001001(137) Y=11101110 (238)
因此,X & Y=10001000 (136)
按位与的特殊用途:
1、清零。
方法,与一个各位都为零的数值相与,结果为零。
2、取一个数 x中某些指定位。
方法,找一个数,此数的各位是这样取值的:对应 x数要取各位,该数对应位为 1,其余位为零。此数与 x相就可以得到 x中的某些位。
例:设 X=10101110
(1)取 X的低 4位
(2)取 X的 bit2,bit4,bit6位二、“按位或”运算符规则:
参加运算的两个运算量,如果两个相应位中有个为 1,则该位结果值为 1,
否则为 0。
例如 X=10001001(137) Y=11101110(238)
X | Y=11101111(239)
按位或的特殊用途,常用来对一个数据的某些位置 1。
方法:找一个数,此数的各位是这样取值的,对应 x数要置 1的位,该数对应位为 1,其余位为零。此数与 x相或就可使 x中的某些位置 1。
例:使 x=10100000 的低 4位为 1。
三、“异或”运算符规则:参加运算的两个运算量,如果两个相应位为“异” (值不同),
则该位结果值为 1,否则为 0。
例如 X=10001001(137) Y=11101110(238)
X^Y=01100111(103)
异或运算的应用:
( 1)使特定位翻转,找一个数,此数的各位是这样取值的:对应 x数要转的各位,该数对应位为 1,其余位为零。此数与 x相异或即可。
例,x=10101110,使 x低 4位翻转
(2)与 0相异或,保留原值。例、
四、“取反”运算符 ~ (单目运算符 )
对一个二进制数按位取反,即将 0变为 1,1变为 0。
例,0100001110010111 取反 1011110001101000
注意,~运算符的优先级别比算术运算符、关系运算符、逻辑运符和其它运算符都高。
例:使一个数 a的最低位为零,可以表示成:
a & ~ 1
因为,~1=1111111111111110。
五、左移运算符 <<
将一个数的各二进制全部左移若干位。(左丢弃,右补 0)
例,a=a<<2 将 a的二进制数左移 2位,右补 0。
若左移时舍弃的高位不包含 1,则数每左移一位,相当该数乘以 2。
六、右移运算符 >>
将一个数的各二进制位全部右移若干位。(正数左补 0,z负数左补 1,右丢弃)
例,a=a>>2 将 a的二进制数右移 2位。
将一操作数左移一位,相当于将其乘 2。将一操作数右移一位,相当于将其除以 2。
因此,可以用移位操作代替部分乘除操作,只要不产生溢出,这种代替是正确的。
(用 CF标志判别无符号数运算是否溢出,CF=1,表示溢出。
用 OF标志判别有符号数运算是否溢出,OF=1,表示溢出)。
七、位运算符与赋值运算符结合,组成展的赋值运算符。
&=,例,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
12.3 位运算举例例 [12.1] 取一个整数 a从右端开始的 4~ 7位。 比如,
0000,0000,1101,1001
(八进制 331/十进制 217,4~ 7位 1101的八进制值是 15/十进制 13)
方法:
( 1)先使 a右移 4位,使要取出的几位移到最右端。 a>>4
( 2)设置一个低 4位全为 1,其余为 0 的数 ~( ~0<<4)
( 3)将上面两者进行 &运算。
程序:
main()
{
unsigned a,b,c,d;
scanf(“%o”,&a);
b = a >> 4;
c = ~(~0 << 4); /* 0x000f */
d = b & c;
printf(“%o\n%o\n”,a,b);
}
例 [12.2] 将 a进行循环右移,如原来右端 3位移到最左端 3位。
1101111110101011 --> 0111101111110101
注意,不能直接使用 >>运算符,因为,>>运算符使左面添 0或添 1。
算法:设需要循环右移的数是一个整数 (两字节 )。移动 n位
( 1)将 a的右端 n位先放到中间变量 b的高 n位中。 (左移 16-n位 )
b= a<<(16-n);
( 2)将 a右移 n位,其左面 n位补 0。
c=a>>n;
( 3)将 c与 b 进行按位或运算。
c=a | b
程序:
void main()
{
unsigned a,b,c;
int n;
scanf("a=%o,n=%d",&a,&n);
b=a<<(16-n);
c=a>>n;
c=c|b;
printf(“%o\n%o”,a,c);
}
12.4 位段信息可以用用 1字节,2字节,4字节,8字节表示。例如,用 1字节表示一个英文字符,2字节表示一个汉字字符,4字节表示一个实数,....等。
某些信息可以用一位二进制表达,如:
打印机状态:
bit0,打印机在规定的时间内未完成上一次指定的操作。
bit5,打印机当前缺纸。
bit7,打印机当前准备好。
位段的概念与定义所谓位段类型,是一种特殊的结构类型,其所有成员均以二进制位为单位定义长度,并称成员为位段。
例如,CPU的状态寄存器,按位段类型定义如下:
struct status
{ unsigned sign:1; /*符号标志 */
unsigned zero:1; /*零标志 */
unsigned carry:1; /*进位标志 */
unsigned parity:1; /*奇偶 /溢出标志 */
unsigned half_carry:1; /*半进位标志 */
unsigned negative:1; /*减标志 */
} flags;
显然,对 CPU的状态寄存器而言,使用位段类型(仅需 1个字节),比使用结构类型(需要 6个字节)节省了 5个字节。
某些信息也可以用几位二进制表达。例如,数学协处理器的控制寄存器,有如下结构:
BIT5~ BIT0:异常屏蔽位。置 0时把相应的异常传送给 CPU的异常处理程序,
置 1时把相应的异常传送给协处理器的异常处理程序。
PC,Precision Control,精度控制,指定有效数字的位数(符号位加尾数):
00,24位(短实数)
01:保留
10,53位(长实数)
11,64位(临时实数)
RC,Rounding Control,舍入控制
00,舍入到最接近的值(缺省)
01:舍入到负无穷
10:舍入到正无穷
11:截断在C语言中,为了方便访问这些信息,定义了“位段”
类型,它是用结构体来表示的。
例 1、
struct FPU
{
unsigned,4; /* 无名位段,占 4位 */
unsigned RC:2 /* RC位段,占 2位 */
unsigned PC:2;
unsigned,2; /* 无名位段,占 2位 */
unsigned PM:1; /* PM位段,占 1位 */
unsigned UM:1;
unsigned OM:1;
unsigned ZM:1;
unsigned DM:1;
unsigned IM:1;
};
例2、
struct packed_data
{
unsigned a:2;
unsigned b:6;
unsigned c:4;
unsigned d:4;
int i;
} data
例3、各个位段不恰好占一个字节
struct packed_data
{
unsigned a:2;
unsigned b:3;
unsigned c:4;
int i;
} data
位段的引用:
如,在例3中,可以给位段赋值,
data.a = 2; /* 注意,位段 a占 2位,允许值的范围是 0~ 3 */
data.b = 7; /* 注意,位段 b占 3位,允许值的范围是 0~ 7 */
data.c = 9; /* 注意,位段 c占 4位,允许值的范围是 0~ 15 */
printf("%d %d %d",data.a,data.b,data.c);
说明
( 1)因为位段类型是一种结构类型,所以位段类型和位段变量的定义,
以及对位段(即位段类型中的成员)的引用,均与结构类型和结构变量一样。
( 2)对位段赋值时,要注意取置范围。一般地说,长度为 n的位段,其取值范围是,0~( 2n-1)。
( 3)使用长度为 0的无名位段,可使其后续位段从下 1个字节开始存储。
例如
struct status
{ unsigned sign:1; /*符号标志 */
unsigned zero:1; /*零标志 */
unsigned carry:1; /*进位标志 */
unsigned,0; /*长度为 0的无名位段 */
unsigned parity:1; /*奇偶 /溢出标志 */
unsigned half_carry:1; /*半进位标志 */
unsigned negative:1; /*减标志 */
} flags;
原本 6个标志位是连续存储在 1个字节中的。由于加入了 1个长度为 0的无名位段,所以其后的 3个位段,从下 1个字节开始存储,一共占用 2个字节。
( 4) 1个位段必须存储在 1个存储单元(通常为 1字节)中,不能跨 2
个。如果本单元不够容纳某位段,则从下 1个单元开始存储该位段。
( 5)可以用 %d,%x,%u和 %o等格式字符,以整数形式输出位段。
( 6)在数值表达式中引用位段时,系统自动将位段转换为整型数。
本章要求及作业
1、位运算及其特殊用途。
2、信息的位段表示。位段数据类型。
作业,12.1