Presentation is loading. Please wait.

Presentation is loading. Please wait.

第 12 章位运算 C 语言兼具高级语言及低级语言的特性,因此 适合编写系统软件。 C 语言具备低级语言的特性 就在于它能直接对硬件进行操作,即位运算。 所谓位运算是指,按二进制位进行的运算。 例如,将一个存储单元中各二进位左移或右移一 位等。

Similar presentations


Presentation on theme: "第 12 章位运算 C 语言兼具高级语言及低级语言的特性,因此 适合编写系统软件。 C 语言具备低级语言的特性 就在于它能直接对硬件进行操作,即位运算。 所谓位运算是指,按二进制位进行的运算。 例如,将一个存储单元中各二进位左移或右移一 位等。"— Presentation transcript:

1 第 12 章位运算 C 语言兼具高级语言及低级语言的特性,因此 适合编写系统软件。 C 语言具备低级语言的特性 就在于它能直接对硬件进行操作,即位运算。 所谓位运算是指,按二进制位进行的运算。 例如,将一个存储单元中各二进位左移或右移一 位等。

2 12.1 数值在计算机中的表示 1. 二进制位与字节 计算机系统的内存储器,是由许多称为字节的单元组 成的, 1 个字节由 8 个二进制位( bit )构成,每位的取值为 0/1 。最右端的那 1 位称为 “ 最低位 ” ,编号为 0 ;最左端的那 1 位称为 “ 最高位 ” ,而且从最低位到最高位顺序,依次编号。 图 11-1 是 1 个字节各二进制位的编号。 图 11-1 1 个字节各二进制位的编号 2. 数值的原码表示 数值的原码表示是指,将最高位用作符号位( 0 表示正 数, 1 表示负数),其余各位代表数值本身的绝对值(以二 进制形式表示)的表示形式。为简化描述起见,本节约定 用 1 个字节表示 1 个整数 。 76543210

3 例如, +9 的原码是 00001001 └→ 符号位上的 0 表示正数 -9 的原码是 10001001 。 └→ 符号位上的 1 表示负数 3. 数值的反码表示 数值的反码表示分两种情况: ( 1 )正数的反码:与原码相同。 例如, +9 的反码是 00001001 。 ( 2 )负数的反码:符号位为 1 ,其余各位为该数绝 对值的原码按位取反( 1 变 0 、 0 变 1 )。 例如, -9 的反码:因为是负数,则符号位为 “1” ;其 余 7 位为 -9 的绝对值 +9 的原码 0001001 按位取反为 1110110 , 所以 -9 的反码是 11110110 。

4 4. 数值的补码表示 数值的补码表示也分两种情况: ( 1 )正数的补码:与原码相同。 例如, +9 的补码是 00001001 。 ( 2 )负数的补码:符号位为 1 ,其余位为该数绝对值 的原码按位取反;然后整个数加 1 。 例如, -9 的补码:因为是负数,则符号位为 “1” ;其余 7 位为 -9 的绝对值 +9 的原码 0001001 按位取反为 1110110 ;再 加 1 ,所以 -9 的补码是 11110111 。 已知一个数的补码,求原码的操作分两种情况: ( 1 )如果补码的符号位为 “0” ,表示是一个正数,所 以补码就是该数的原码。 ( 2 )如果补码的符号位为 “1” ,表示是一个负数,求 原码的操作可以是:符号位不变,其余各位取反,然后再 整个数加 1 。

5 例如,已知一个补码为 11111001 ,则原码是 10000111 ( -7 ):因为符号位为 “1” ,表示是一个 负数,所以该位不变,仍为 “1” ;其余 7 位 1111001 取反后为 0000110 ;再加 1 ,所以是 10000111 。 5. 数值在计算机中的表示 ── 补码 在计算机系统中,数值一律用补码表示(存 储),

6 12.2 位 运 算 12.2.1 位运算及其运算符 1 .按位与 ──& (1) 格式: x&y (2) 规则:对应位均为 1 时才为 1 ,否则为 0:3&9=1 。 例如, 3&9=1 : 0011 & 1001 ──── 0001=1 (3) 主要用途:取 ( 或保留 )1 个数的某 ( 些 ) 位,其余各位置 0 。 2 .按位或 ──| (1) 格式: x|y (2) 规则:对应位均为 0 时才为 0 ,否则为 1 : 3|9=11 。 例如, 3|9=11 : 0011 | 1001 ──── 1011=11 (3) 主要用途:将 1 个数的某 ( 些 ) 位置 1 ,其余各位不变。

7 3 .按位异或 ──^ (1) 格式: x^y (2) 规则:对应位相同时为 0 ,不同时为 1 : 3^9=10 。 (3) 主要用途:使 1 个数的某 ( 些 ) 位翻转 ( 即原来为 1 的位 变为 0 ,为 0 的变为 1) ,其余各位不变。 4 .按位取反 ──~ (1) 格式: ~x (2) 规则:各位翻转,即原来为 1 的位变成 0 ,原来为 0 的 位变成 1 :在 IBM-PC 机中, ~0 = 0xffff , ~9=0xfff6 。 (3) 主要用途:间接地构造一个数,以增强程序的可移 植性。 5 .按位左移 ──<< (1) 格式: x<< 位数 (2) 规则:使操作数的各位左移,低位补 0 ,高位溢出: 5<<2=20 。

8 6 .按位右移 ──>> (1) 格式: x>> 位数 (2) 规则:使操作数的各位右移,移出的低位舍弃; 高位: 1) 对无符号数和有符号中的正数,补 0 ; 2) 有符号数中的负数,取决于所使用的系统:补 0 的 称为 “ 逻辑右移 ” ,补 1 的称为 “ 算术右移 ” 。例如, 20 >> 2=5 。 说明: ( 1 ) x 、 y 和 “ 位数 ” 等操作数,都只能是整型或字符 型数据。除按位取反为单目运算符外,其余均为双目运 算符。 ( 2 )参与运算时,操作数 x 和 y ,都必须首先转换成 二进制形式,然后再执行相应的按位运算。 例如, 5 > 2=5 : 10100 → 00101 。

9 ( 3 )实现 & 、 | 、 ^ 运算主要用途的方法 1 )构造 1 个整数:该数在要取(或保留)的位、或 要置 1 的位、或要翻转的位上为 1 ,其余均为 0 。 2 )进行按位与、或按位或、或按位异或操作。 ( 4 )实现按位取反主要用途的方法 1 )求 ~0 ,间接地构造一个全 1 的数; 2 )按需要进行左移或右移操作,构造出所需要的数。 例如,直接构造一个全 1 的数,在 IBM-PC 机中为 0xffff ( 2 字节),而在 VAX-11/780 上,却是 0xffffffff ( 4 字节)。如果用 ~0 来构造,系统可以自动适应。具体应 用,请参见 [ 案例 11.1] 。 12.2.2 应用举例 [ 案例 11.1] 从键盘上输入 1 个正整数给 int 变量 num ,输 出由 8 ~ 11 位构成的数(从低位、 0 号开始编号)。

10 基本思路: ( 1 )使变量 num 右移 8 位,将 8 ~ 11 位移到低 4 位上。 ( 2 )构造 1 个低 4 位为 1 、其余各位为 0 的整数。 ( 3 )与 num 进行按位与运算。 /* 案例代码文件名: AL11_1.C*/ /* 程序功能:输出一个整数中由 8 ~ 11 位构成的数 */ main() { int num, mask; printf("Input a integer number: "); scanf("%d",&num); num >>= 8; /* 右移 8 位,将 8 ~ 11 位移到低 4 位上 */ mask = ~ ( ~0 << 4); /* 间接构造 1 个低 4 位为 1 、其余各位为 0 的整数 */ printf("result=0x%x\n", num & mask); }

11 程序运行情况: Input a integer number:1000 ←┘ result=0x3 程序说明: ~ ( ~0 << 4) 按位取 0 的反,为全 1 ;左移 4 位后,其低 4 位为 0 ,其 余各位为 1 ;再按位取反,则其低 4 位为 1 ,其余各位为 0 。 这个整数正是我们所需要的。 [ 案例 11.2] 从键盘上输入 1 个正整数给 int 变量 num ,按 二进制位输出该数。 /* 案例代码文件名: AL11_2.C*/ /* 程序功能:按二进制位输出一个整数 */

12 #include "stdio.h" main() { int num, mask, i; printf("Input a integer number: "); scanf("%d",&num); mask = 1<<15; /* 构造 1 个最高位为 1 、其余各位为 0 的整数 ( 屏蔽字 )*/ printf("%d=", num); for(i=1; i<=16; i++) { putchar(num&mask ? ’1’ : ‘0’);/* 输出最高位的值 (1/0)*/ num <<= 1; /* 将次高位移到最高位上 */ if( i%4==0 ) putchar(‘,’); /* 四位一组,用逗号分开 */ } printf("\bB\n"); }

13 程序运行情况: Input a integer number:1000 ←┘ 1000=0000,0011,1110,1000B 12.2.3 说明 1. 复合赋值运算符 除按位取反运算外,其余 5 个位运算符均可与赋值运算 符一起,构成复合赋值运算符: &= 、 |+ 、 ^= 、 >= 2. 不同长度数据间的位运算 ── 低字节对齐,短数的高 字节按最高位补位: ( 1 )对无符号数和有符号中的正数,补 0 ; ( 2 )有符号数中的负数,补 1 。

14 12.3 位段简介 有时,存储 1 个信息不必占用 1 个字节,只需二进制的 1 个(或多 个)位就够用。如果仍然使用结构类型,则造成内存空间的浪费。 为此, C 语言引入了位段类型。 1. 位段的概念与定义 所谓位段类型,是一种特殊的结构类型,其所有成员均以二进 制位为单位定义长度,并称成员为位段。 例如, CPU 的状态寄存器,按位段类型定义如下: struct status { unsigned sign: 1; /* 符号标志 */ unsigned zero: 1; /* 零标志 */ unsigned carry: 1; /* 进位标志 */ unsigned parity: 1; /* 奇偶 / 溢出标志 */ unsigned half_carry: 1; /* 半进位标志 */ unsigned negative: 1; /* 减标志 */ } flags;

15 显然,对 CPU 的状态寄存器而言,使用位段类型(仅需 1 个字节),比 使用结构类型(需要 6 个字节)节省了 5 个字节。 2. 说明 ( 1 )因为位段类型是一种结构类型,所以位段类型和位段变量的定义, 以及对位段(即位段类型中的成员)的引用,均与结构类型和结构变量一 样。 ( 2 )对位段赋值时,要注意取置范围。一般地说,长度为 n 的位段,其 取值范围是: 0 ~( 2 n -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;

16 原本 6 个标志位是连续存储在 1 个字节中的。 由于加入了 1 个长度为 0 的无名位段,所以其后的 3 个位段,从下 1 个字节开始存储,一共占用 2 个 字节。 ( 4 ) 1 个位段必须存储在 1 个存储单元(通常 为 1 字节)中,不能跨 2 个。如果本单元不够容纳 某位段,则从下 1 个单元开始存储该位段。 ( 5 )可以用 %d 、 %x 、 %u 和 %o 等格式字符, 以整数形式输出位段。 ( 6 )在数值表达式中引用位段时,系统自动 将位段转换为整型数。


Download ppt "第 12 章位运算 C 语言兼具高级语言及低级语言的特性,因此 适合编写系统软件。 C 语言具备低级语言的特性 就在于它能直接对硬件进行操作,即位运算。 所谓位运算是指,按二进制位进行的运算。 例如,将一个存储单元中各二进位左移或右移一 位等。"

Similar presentations


Ads by Google