15.位操作

一、二进制数、位、字节

1、二进制整数

C语言使用byte存储系统字符集所需要的大小,可以是8位,16位等;

描述存储器芯片和传输速率所用的字节:8位字节;

1字节(byte)=8位(bit);

二进制数:以2为基地表示的数字;

例如,二进制数:1101 可表示为:1*2^3+1*2^2+0*2^1+1*2^0

假设这里是1byte=8bit,需要从左往右依次编号为7~0;

高阶位(high-order bit):是编号7,低阶位(low-order bit):是编号0;

该字节最小二进制数:00000000,最大二进制数:11111111;

因此,1byte可存储0~255范围内的数字,共256个值;

选用不同方式解释为组合(bit pattern):

unsigned char :(1byte)0~255;

signed char:(1byte)-128~+127;

表示有符号整数(signed int)取决于硬件,表示方法;

1)符号量表示法

高阶位存储符号,只剩下7位表示数字本身;

10000001表示:-1

000000001表示:1

因此,表示范围为-127~+127;

此方法缺点:有-0,+0两个0;

2)二进制补码

用1byte中后7位表示0~127,高阶位设置为0;

高阶位:1,表示值为负

二进制补码与符号量表示法区别不同在于如何确负值

9位组合100000000(256的二进制形式)- 负数的位组合=该负值的量

例如:

无符号字节:10000000,该组合表示128,是一个负值的位组合

作为有符号值:是第7位表示1:1000000

100000000-1000000=1000000(128)

总的来说,使用二进制补码表示负数,可以通过正数:反转每一位(0取1,1取0),然后+1得到对应的相反数

例如,00000001是1,那么-1就是11111110+1就是11111111

3)二进制反码

正数与原码相同,负数是原码取反

这种方法也会有一个-0;

00000001是1,11111110是-1;

该方法能表示-127~+127;

二、其他进制数

1、二进制浮点数

浮点数存储分为两部分存储:二进制小数部分,二进制指数部分

普通十进制:0.527

5/10+2/100+7/1000

二进制小数.101

1/2+0/4+1/8

注意,二进制表示法只能精准表示1/2幂的和

根据以上规律可得到,数字的实际值是由二进制小数乘以 2的指定次幂组成;

就有,一个浮点数乘以4,那么二进制小数不变,其指数乘以2,二进制分数不变。

2、八进制(octal)

该系统基于8的幂,用0~7表示 数字(正如十进制用0~9表示数字一样);

每个八进制位对应3个二进制位;

3、十六进制(hex)

该系统基于16 的幂,用0~15表示数字;单独数表示0~9,字母A~F来表示10~15;

每个十六进制位都对应一个4位的二进制数(即4个二进制位),那么两 个十六进制位恰好对应一个8位字节。

三、C按位运算符

1、按位逻辑运算符

四个按位逻辑运算符都用于整型数据,包括char,针对每一个位进行;

1)按位取反~

该运算符不会改变原有的值,即

newval = ~val;(val的值不变)

2)按位与&

二元运算符&通过逐位比较两个运算对象,生成一个新值;

拓展:按位与和赋值结合的运算符:&=

val &= 0377;

val = val & 0377;

3)按位或|

二元运算符 | 通过逐位比较两个运算对象,生成一个新值;

拓展:按位或和赋值结合的运算符:|=

val |= 0377;

val = val | 0377;

4)按位异或^

二元运算符^逐位比较两个运算对象

a、b异或的比较结果:

abresult
101
011
110
000

拓展:按位异或和赋值结合的运算符:^=

val ^= 0377;

val = val ^ 0377;

2、移位运算符

1)左移<<

左移运算符(<<)将其左侧运算对象每一位的值向左移动其右侧运算对象指定的位数

左侧运算对象移出左末端位的值丢失,用0填充空出的位置。

(10001010) << 2 // 表达式

(00101000) // 结果值

2)右移>>

右移运算符(>>)将其左侧运算对象每一位的值向右移动其右侧运算 对象指定的位数。

对于无符号类型,用 0 填充空出的位置;对于有符号类型,其结果取决于机器。

特别的,移位运算符针对2的幂提供快速有效的乘法和除法:

number << n  number乘以2的n次幂

number >> n 如果number为非负,则用number除以2的n次幂

3、常见用法

1)掩码(&)

指一些设置位开或关(1、0)的位组合:掩盖其他位,只显示特定位;

例如,MASK=2(00000010);

1号位为1,其他位为0;

flags = flags & MASK

作用:掩盖flags除1号位以外的位,都会为0,对于1号位,根据flags本身值决定

常见:ch&=0xff;或者ch&= 0377

0xff是1111 1111;0377是000 011 111 111

这个操作会保持ch的最后8位保持不变

2)打开位(|)

值保持其他位不变,设置一个特定位

根据特性,任何位与1组合,为1(MASK可以有很多种可能)

例如,设置1号位为1,其他位保持不变;

MASK=0000 0010(0110 1111), flags= 0110 1101

(MASK设为1的位,flags对应设为1,MASK设为0的位,flags保持不变)

flags = flags | MASK;

结果为:0110 1111

3)关闭位(&~)

在不影响其他位的情况,关闭指定的位

当要关闭flags中的1号位,且MASK只有1号位为1,则有

flags = flags & ~MASK;

MASK 0000 0010

~MASK=1111 1101

结果  0000 1101

根据&~特性可以设计其他MASK的值

例如,

flags是0000 1111,MASK是1011 100。

flags = flags & ~MASK;

~MASK=0100 1101

结果:0000 1101

4)切换位(^)

可用于:打开关闭的位,关闭打开的位

将flag的与MASK为1的位对应切换,则有

例如,假设flags是0000 1111,MASK是1011 0110。表达式:

flags ^= MASK;

结果:1011 1001

flags中与MASK为1的位相对应的位都被切换了,MASK为0的位相对应 的位不变。

5)检查位的值

有时,需要检查某位的值。但又不能直接比较

常作用:覆盖flags中其他位,只用1号位做比较

if ((flags & MASK) == MASK)

四、位字段

操控位的第2种方法是位字段(bit field);

位字段是一个signed int/unsigned int类型变量中的一组相邻的位;

(C99和C11新增了_Bool类型的位字段);

位字段通过一个结构声明来建立,提供标签和宽度;

struct {
unsigned int autfd : 1;
unsigned int bldfc : 1;
unsigned int undln : 1;
unsigned int itals : 1;
} prnt;

可以通过普通的结构成员 运算符(.)单独给这些字段赋值:

prnt.itals = 0;
prnt.undln = 1;

带有位字段的结构提供一种记录设置的方便途径。许多设置(如,字体 的粗体或斜体)就是简单的二选一。例如,开或关、真或假。如果只需要使用 1 位,就不需要使用整个变量;

内含位字段的结构允许在一个存储单元中储存多个设置;

注意,如果声明的总位数超过了一个unsigned int类型,会用到下 一个unsigned int类型的存储位置;

一个字段不允许跨越两个unsigned int之间的边界;

编译器会自动移动跨界字段,并与边界对齐,那么第一个unsigned int会留下一个未命名的”洞“;

可以用未命名的字段宽度“填充”未命名的“洞;

struct {
unsigned int field1 : 1 ;
unsigned int : 2 ;
unsigned int field2 : 1 ;
unsigned int : 0 ;
unsigned int field3 : 1 ;
} stuff;

这里,在stuff.field1和stuff.field2之间,有一个2位的空隙;

另外,switch语句中也可以使用位字段成员,甚至还可以把位字段成员用作数组的下标:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值