C 学习笔记 —— 位操作与位运算

概述

单独操控位,有时向硬件设备发送一两个字节来控制这些设备,其中每个位都有特定含义。

一个字节:从左到右8位编号分别为7~0。
编号7被称为高位,编号0被称为低位。
一字节有8bit,2的8次方256个值。通常无符号1byte能表示的数范围是0~255.有符号表示范围-128 —— +127

有符号和无符号数

无符号数

无符号数都是从0开始,表示的范围是他的2次方位数的最大值。
所以无符号1byte能表示的数范围是0~255.

有符号

如何表示有符号数取决于硬件,而不是某种语言。
有下面两种表示法,我们对比一下

高位表示负数法

最简单方式,我们使用一位表示符号位,所以2的7次方是128。关键在于我们如何表示负数,如果我们使用二进制反码的形式来表示,比如说00000001表示+1,他的取反11111110表示-1.

这种方式有个缺点:
这时+0和-0就有两个数了。浪费了一个值的表示。 只能表示则是-127-0和0- +127。范围是-127 - +127。

二进制反码同理,也会浪费一个数值。

二进制补码

为了避免这种情况,有符号数使用二进制补码形式。这也是当今最常用的硬件表示负数方法。
我们使用取反加一,来确定一个负值。

  1. 表示正数:符号位是0则后7位表示0-+127. 0的表示为00000000。127表示为01111111,+1表示为00000001。和无符号方式相同
  2. 表示负数:我们用正数的取反加一来表示负数。如果符号位设为1,-127表示就是127的取反加1,为10000001。-1表示就是+1的取反加1,11111111. 而0的取反加一表示为将10000000,因为有0了,所以我们将这个数作为最大负值也就是-128,所以有符号位负数表示范围是-128 - -1,而没有0.

这样使用二进制补码我们就可以表示-128 - + 127的范围的数了。

扫描二维码关注公众号,回复: 14802175 查看本文章

位操作

把某个变量进行位操作,并不是直接修改这个变量的值,而是位操作后返回一个新值,原值不变。
比如取反运算:
newVal = ~val;
如果想改变原值则:val = ~val;

位操作主要有三种:

  1. 与&, A&B, 有0则为0
  2. 或|,A|B,有1则为1
  3. 异或 ^, 相同为0,不同为1

移位运算符

左移

将所有值向左移动,左边移出的值就不要了,右边用0填充空位。
比如一个二进制数左移2位
10001010 << 2 = 00101000;

用法

移位运算符主要是针对2的幂次方实现快速乘除法。
number << n; // number乘以2的n次方

左移0,不变。左移1,十进制乘2,左移2,十进制乘2的2次方。
int a = 1;
a <<= 2; //a = 4;

右移

将所有值向右移动,移出的值就不要了,左边用0填充,但是如果是有符号数,则最高位可能填的是1或者0,取决于硬件。
右移0,十进制不变,右移1,十进制除2,右移2,十进制除2的2次方,右移3,十进制除2的3次方。

用法

举例1:
number >> n; // 如果值为正数,则number除以2的n次方
int a = 16;
a >>= 2; //a = 4;

举例2:
我们还可以针对十六进制数直接移动,可以更直观

unsigned long color = 0x002a162f;
color >>= 8; //0x00002a16。 因为右移8位正好是十六进制两个数表示8位,所以直接将color末尾的2f去掉即可。
color &= 0xff; //0x16。这时使用&运算符保留位,可以看到我们保留的是十六进制最后两个数,所以得到0x16.

移位负值

如果移动的值为负值,则这个操作的行为由编译器来决定,他是不确定的。

a << -5;

检查一个数的指定位是否为1,并设置1

我要检查val的第几位是否为1:肯定要使用&运算符
这里i是要检查的第几个bit位。

int val = 355;
if ( (val >> i) & 1 == 1) {
    
    
}

当我们设置位为1的时候,肯定要使用 | 运算符
比如我要设置val的第i位为1

int val = 355;
val |= (1 << i);

如果i=4,也就是我们想将val的第四位设置为0,
那么(1<<4) 表示1左移4位则二进制为 00010000;
这时val |= (1 << i); 就表示将val的第4个比特位设置为1,其他位保持不变。

用法汇总

掩码,保留位(取低八位)&

掩码指的是设置开关组合,1为开,0为关。
比如我们设置一个byte的掩码 MASK= 00000010; 这里mask只有1号为是1.
如果我们想把一个byte的变量flag中的除1号位其他所有位都设置为0。我们可以这样:
flag = flag & MASK;
可以简化为flag &= MASK;
使用&运算符,就可以将flag除1号位的其他位都置为0;
这就是掩码的含义,将flag中其他所有位都隐藏,只留下我们指定的某个位,这个位在掩码中用1表示,1表示透明,0表示掩盖。
在这里插入图片描述

再举个常见例子:
int a &= 0xff;
这里我们可以看到掩码宽度为8bit。
这里的作用:保留变量ch的后八位,(因为16进制一个数表示4bit)。变量a是int类型一共有32位,将前面的24为都置为0,只保留后八位,取低八位的值。

检查位的值 &

比如,我要检查一个变量的1号位是否为1.
这样做

MASK = 2; //00000010
if ((flag & MASK) == MASK) {
    
    
	puts("check");
}

检查flag中的1号位是否被设置为1。

清零位 & ~

比如MASK= 00000010;
flag &= ~MASK;
因为直接使用&=是保留MASK中指定的位,而取反,就是清零MASK中指定的位,保留MASK其他位。

设置位 |

可以用来设置某一位的值为1.
比如MASK= 00000010;
flag |= MASK;
目的就是把flag的1号位给设置为1. 其他位保持不变

切换位 ^

flag ^= MASK;
将flag中与MASK对应为1的位都切换,MASK为0的位保持不变。

猜你喜欢

转载自blog.csdn.net/chongbin007/article/details/126358072
今日推荐