Giskard

(三十三)位操作

2018-11-01

一个字节由8个位表示,0000 0000 == 0 == 0x00,0000 0001 == 1 == 0x01……1111 1111 == 255 == 0xFF

从左到右称为bit7,bit6,bit5…bit0

引例1

舞台上有8盏灯,用1表示亮,0表示灭

  • 第一种方法

    int status[8] = {1,0,0,1,0,0,0,1};//32个字节
    
  • 第二种方法

    char status[8] = {1,0,0,1,0,0,0,1};//8个字节
    

显然,第二种占用空间小

利用位bit来表示信息,占用空间可以更小

unsigned char status = 0x91;  //1001 0001,仅需一个字节!

引例2

用变量表示在校学生的出生年月日

  • 第一种方法

    int year = 1982,month=10,day=23;//需要12字节
    
  • 第二种方法

    short year = 1982;char month = 10;char day = 23;//需要4字节,short要2个字节
    

用7个位表示年000 0000 - 111 1111(0-127):将1982记为82

用4个位表示月0000-1111(1-15)

用5个位表示日0 0000-1 1111(0-31)

上面的一共才需要16位,即2个字节

优点与缺点

  • 优点:占用空间小
  • 缺点:读写麻烦,不容易修改/读取其中一个位的状态

应用场景

嵌入式(GPIO,一个位连着一个电线)

数据压缩编码(音视频,网络协议)

移位

位操作是对无符号数进行操作的

unsigned char a = 0x13;
//0001 0011 -> 0100 1100,左侧移出,右侧补0
unsigned char b = a << 2;
//0001 0011 -> 0000 0100,右侧移出,左侧补0
unsigned char c = a >> 2;
unsigned int flag = 1u << 7;
//1是int类型的,1u才算unsigned int类型的,这里为1u

左移一位相当于乘2

右移一位相当于除2

移位运算比乘除运算速度更快,节省资源

移位并赋值运算符:<<= 、 >>=

a <<= 2相当于a = a << 2

有符号整数的移位没有实际意义

char c = 1;
c = c << 7;
//c应该是1000 0000,但是他的结果却是-128

取反

unsigned char a = 0x13;//0001 0011
unsigned char b = ~a;  //1110 1100

与操作

unsigned char result = a & b;
应用场景1:判断某位的值
unsigned char a = 0x13;//0001 0011

怎么判断bit4是亮还是灭

unsigned char flag = status & 0x10;//0001 0000
if(flag)
{
    //亮
}

怎么判断应该任意位的值

bool checkBit(unsigned char a,int N)
{
    unsigned char mask = 1u << N;//1左移N位
    return a & mask;
}

例如a为0001 0011求bit2,N为2

mask就变成0000 0100,a&mask为0001 0011 & 0000 0100为0,故bit2为0

应用场景2:按位清零
unsigned char a = 0x6A;//0110 1010

要把bit2和bit3设为0,只要a&0xF3就行(0xF3为1111 0011,是凑出来的)

应用场景3:读取多个位
unsigned char a = 0x6A;//0110 1010

要取出bit2…bit5怎么做

  • 右移两位,0110 1010->0001 1010

    unsigned char b = a >> 2;
    
  • 高位清0,0001 1010->0000 1010

    unsigned char c = b & 0x0F;
    

合在一起

unsigned char result = (a>>2)&0x0F;
综合应用:读取年月日
//1982,10,23
//年82,月10,日23
//上面有,高7位表示年,后面4位为月,最后5位为日,共两个字节
//1010010 1010 10111
//合起来分成2个字节
//10100101 01010111
//即为0xA5,0x57

unsigned char data[2] = {0xA5,0x57};
int year = data[0]>>1;
int month = ((data[0]&0x01)<<3)+(data[1]>>5);
int day = data[1]&0x1F;

####或操作

unsigned char result = a | b;
应用场景:置1
unsigned char status = 0x13;//0001 0011
//让bit4亮起来
status |= 0x10;
//让bit2...bit4亮起来
status |= 0x1C;

位操作应用:base64编码

#####64进制

base64就是64进制,包括以下字符

A..Z , a..z , 0..9 , + /

26+26+10+2=64

1/代表的十进制为53*64+63

base64编码:将任意数据转化为文本

规则:每6位一组,转成base64字符

unsigned char data[1024];

例如

4D 5A 90->01001101 01011010 10010000

划分为4组->010011 010101 101010 010000

得到4个数,每个数介于(000000-111111)(0-63)之间

再以64进制表示19(T),21(V),42(q),16(Q)

故为TVqQ

AfBase64

实现base64编码的类,与加密解密有关

Tags: C/C++