综述


程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。运位算包括位逻辑运算和移位运算,位逻辑运算能够方便地设置或屏蔽内存中某个字节的一位或几位,也可以对两个数按位相加等;移位运算可以对内存中某个二进制数左移或右移几位等。

计算机内部是以补码形式存放数值的。

c语言中存在6个位操作运算符,且它们只能用于整形操作数。

位操作符 名称
& 按位与
| 按位或
^ 按位异或
<< 按位左移
>> 按位右移
~ 按位取反

按位与:&


  1. 按位与的定义是:同一二进制位上的数字都是1的话,&的结果为1,否则为0.

    0 & 0 = 0;

    0 & 1 = 0;

    1 & 1 = 1;

    不同大小的数据位操作的原则,低位对齐,高位补零

  2. 根据这个特性,&操作常常用来屏蔽特定的二进制位。例如:

    ​ 0000 1111 & 0000 0011

    ​ 结果为0000 0011.可以看见,1111的前两位被屏蔽成为0了。

    ​ 所以如果想清空数据,只需要将原二进制数与上0就可以了。0的位数对应原二进制数的位数,对各位进行屏蔽,全部置0.

    ​ 相对的,&可以利用0来屏蔽,也可以用1来读取。

    ​ 例如: 一个二进制数 1101 1001,我只想要它的后四位,怎么办呢?

    ​ 只需要进行如下操作:1101 1001 & 0000 1111即可。

    ​ 其实该方法是屏蔽和读取的结合,&0保证消除无用位,&1保证有用数据的完整性。

    ​ 总结:对于原二进制数来说,&0是屏蔽,&1是不变。

按位或(OR):|


  1. 定义:只要参与运算的双方其中有一个是1,结果就是1.同0才为0.

    0 | 0 = 0;

    0 | 1 = 1;

    1 | 0 = 1;

    1 | 1 = 1;

    不同大小的数据位操作的原则,低位对齐,高位补零。

  2. 将某些特定位置1.

    例如:1010 0000 | 0000 1111.

    结果为 1010 1111.

    总结:对于原二进制数来说,|0是不变,|1是置1.

按位异或:^


  1. 只要参与运算的双方互异,结果就为1,否则为0.

    0 ^ 1 = 1;

    1 ^ 0 = 1;

    1 ^ 1 = 0;

    0 ^ 0 = 0;

    不同大小的数据位操作的原则,低位对齐,高位补零。

  2. 用法

    可以通过上面的定义看到,一个数1的话就会0变成1,1变成0,而0则不对原数进行改变。所以根据此特性可以对特定位进行0 1 反转。

    例如: 1100 1100 ^ 0000 1100

    结果为 1100 0000.

    同样的,如果对一个数进行^0,代表保留原值。

取反(~)


​ 对一个二进制数进行取反。1变0,0变1.

​ 唯一需要注意的一点是,~的优先级是逻辑运算符中最高的,必须优先计算。

左移<<


  1. 用法

    对运算符<<左边的运算量的每一位全部左移右边运算量表示的位数,右边空出的位补0。

    左移<<的原则是高位舍弃,低位补零。

    例:char a=0x21;

    则a<<2的过程 0010 0001〈〈2 = 1000 0100;即 a<<2的值为0x84。

    左移1位相当于该数乘以2,左移n位相当于该数乘以2n。

右移>>


  1. 用法

    运算规则:对运算符>>左边的运算量的每一位全部右移右边运算量表示的位数,右边低位被移出去舍弃掉,空出的高位补0还是补1,分两种情况:

    (1)对无符号数进行右移时,空出的高位补0。这种右移称为逻辑右移。

    (2)对带符号数进行右移时,空出的高位全部以符号位填补。即正数补0,负数补1。这种右移称为算术右移。

    右移1位相当于除以2,同样,右移n位相当于除以2n。

    除法运算转化成位运算 (在不产生溢出的情况下)

    a / (2^n) 等价于 a>> n

    取模运算转化成位运算 (在不产生溢出的情况下)

    a % (2^n) 等价于 a & (2^n - 1)

    循环移位的实现。

    如将一个无符号整数x的各位进行循环左移4位的运算,即把移出的高位填补在空出的低位处。

    可以用以下步骤实现:

    (1)将x左移4位,空出的低4位补0,可通过表达式x<<4实现。

    (2)将x的左端高4位右移到右端低4位,可通过表达式x>>(16-4)实现。由于x为无符号整数,故空出的左端补0。

    (3)将上述两个表达式的值进行按位或运算,即:

  y=(x<<4) | (x>>(16-4));x    0010 1111 0010 0001x<<4    1111 0010 0001 0000x>>(16-4) 0000 0000 0000 0010y   1111 0010 0001 0010
unsigned rol ( unsigned a,int n)
{  unsigned b ;b=(a<<n) | (a>>(16-n)) ;return(b);}

​ 计算绝对值

int abs( int x )
{  int y ;y = x >> 31 ;//二进制最高位return (x^y)-y ; //or: (x+y)^y
}

综合灵活运用


在对6种位操作的使用熟悉了之后,我们怎么将其结合起来来获得更加高级和复杂的使用方法呢。

注意,位的索引是从0开始的,最低位为第0位。例如int的位从0-31,0位为最低位,31为最高位。

  1. 指定某一位数为1

    宏 #define setbit(x,y) x|=(1<<y)

    比如将x = 0000 0000 0000 0101的第y = 4位变成1.

    0000 0000 0000 0001<<4 = 0000 0000 0001 0000

    x = x | 0000 0000 0001 0000 = 0000 0000 0000 0101 | 0000 0000 0001 0000 = **0000 0000 0001 0101 **

  2. 指定的某一位数置0

    宏 #define clrbit(x,y) x&=~(1<<y)

    比如将x = 0000 0000 0000 0101的第y = 2位变成0.

    0000 0000 0000 0001<<2 = 0000 0000 0000 0100

    ~(0000 0000 0000 0100) = 1111 1111 1111 1011

    x = x & 1111 1111 1111 1011 = 0000 0000 0000 0101 & 1111 1111 1111 1011= 0000 0000 0000 0001

  3. 指定的某一位数取反

    宏 #define reversebit(x,y) x^=(1<<y)

    比如将x = 0000 0000 0000 0101的第y = 2位由1变成0.

    0000 0000 0000 0001<<2 = 0000 0000 0000 0100

    x = x ^ 0000 0000 0000 0100 = 0000 0000 0000 0101 ^ 0000 0000 0000 0100 = 0000 0000 0000 0001

  4. 获取的某一位的值

    宏 #define getbit(x,y) ((x) >> (y)&1) 或者 #define GET_BIT(x, y) ((x & (1 << y)) >> y)

    比如获取x = 0000 0000 0000 0101的第y = 2位的值.

    0000 0000 0000 0101 >> 2 = 0000 0000 0000 0001

    0000 0000 0000 0001 & 0000 0000 0000 0001 = 0000 0000 0000 0001 = 1

  5. 判断某几位连续位的值

    /* 获取第[n:m]位的值 */
    #define BIT_M_TO_N(x, m, n) ((unsigned int)(x << (31-(n))) >> ((31 - (n)) + (m)))

    这是一个查询连续状态位的例子,因为有些情况不止有0、1两种状态,可能会有多种状态,这种情况下就可以用这种方法来取出状态位,再去执行相应操作。

  6. 获取单字节:

    一个32bit数据的位、字节读取操作

     #define GET_LOW_BYTE0(x)  ((x >> 0) & 0x000000ff)  /* 获取第0个字节 */#define GET_LOW_BYTE1(x)  ((x >> 8) & 0x000000ff)  /* 获取第1个字节 */#define GET_LOW_BYTE2(x)  ((x >> 16) & 0x000000ff)  /* 获取第2个字节 */#define GET_LOW_BYTE3(x)  ((x >> 24) & 0x000000ff)  /* 获取第3个字节 */
    
  7. 清零某个字节:

     #define CLEAR_LOW_BYTE0(x) (x &= 0xffffff00)  /* 清零第0个字节 */#define CLEAR_LOW_BYTE1(x) (x &= 0xffff00ff)  /* 清零第1个字节 */#define CLEAR_LOW_BYTE2(x) (x &= 0xff00ffff)  /* 清零第2个字节 */#define CLEAR_LOW_BYTE3(x) (x &= 0x00ffffff)  /* 清零第3个字节 */
    
  8. 置某个字节为1:

     #define    SET_LOW_BYTE0(x)    (x |= 0x000000ff)    /* 第0个字节置1 */    #define    SET_LOW_BYTE1(x)    (x |= 0x0000ff00)    /* 第1个字节置1 */    #define    SET_LOW_BYTE2(x)    (x |= 0x00ff0000)    /* 第2个字节置1 */    #define    SET_LOW_BYTE3(x)    (x |= 0xff000000)    /* 第3个字节置1 */
    
  9. 高低位交换

    对一个字节数据,逐个交换其高低位,例如11010001,经过0-7,1-6,2-5,3-4对应位的交换,变成10001011 。

    对于该问题,我们最先想到的是对原字节通过移位操作来逐位处理,使用另一个变量来存储交换后的结果。这种解决方案处理起来思路清晰,编写代码应该不难。

    unsigned char shift_fun1(unsigned char data)
    {unsigned char i;unsigned char tmp=0x00;for(i=0;i<8;i++){tmp=((data>>i)&0x01)|tmp;if(i<7)tmp=tmp<<1;}printf("  after shift fun1 data=%x \n",tmp);return tmp;
    }
    

    上述代码实现起来不难,而且效率还是比较高的。但是还有比这更简洁的解决方法,在嵌入式开发中遇到交换字节位的问题时通常使用蝶式交换法和查表法来实现。查表法顾名思义即将一些值存到内存中,需要计算时查表即可,但是也会占用额外的存储空间。这里主要再介绍一下蝶式交换法。

    所谓的蝶式交换是这样的:

    data=(data<<4)|(data>>4);
    data=((data<<2)&0xcc)|((data>>2)&0x33);
    data=((data<<1)&0xaa)|((data>>1)&0x55);
    

    我们可以做一下执行演算:
    假设原始位序列为 0 1 0 1 1 0 0 1

    data=(data<<4)|(data>>4);之后序列为 1 0 0 1 0 1 0 1

    data=((data<<2)&0xcc)|((data>>2)&0x33); 之后序列为 0 1 1 0 0 1 0 1
    data=((data<<1)&0xaa)|((data>>1)&0x55); 之后序列为 1 0 0 1 1 0 1 0

    更抽象的来说 原始位为 1 2 3 4 5 6 7 8

    data=(data<<4)|(data>>4); 之后位序为 5 6 7 8 1 2 3 4
    data=((data<<2)&0xcc)|((data>>2)&0x33); 之后位序为 7 8 5 6 3 4 1 2

    data=((data<<1)&0xaa)|((data>>1)&0x55); 之后位序为 8 7 6 5 4 3 2 1

    由此完成了整个位的逆序转换,下面是具体的实现代码:

    unsigned char shift_fun2(unsigned char data)
    {data=(data<<4)|(data>>4);data=((data<<2)&0xcc)|((data>>2)&0x33);data=((data<<1)&0xaa)|((data>>1)&0x55);printf("  after shift fun2 data=%x \n",data);return data;
    }
    
  10. 判断奇偶数

    对于除0以外的任意数x,使用x&1==1作为逻辑判断即可

  11. 判断一个数是不是 2 的指数

    bool isPowerOfTwo(int n) {if (n <= 0) return false;return (n & (n - 1)) == 0;}
    
  12. 取余,(除数为2的n次方)

    //得到余数int Yu(int num,int n){int i = 1 << n;return num&(i-1);}
    
  13. 统计二进制中 1 的个数

    利用x=x&(x-1),会将x用二进制表示时最右边的一个1变为0,因为x-1会将该位变为0.

    int Count(int x){  int sum=0;while(x){  sum++;x=x&(x-1);}return sum;}
    
  14. 生成组合编码,进行状态压缩

    当把二进制当作集合使用时,可以用or操作来增加元素。合并编码 在对字节码进行加密时,加密后的两段bit需要重新合并成一个字节,这时就需要使用or操作。

  15. 求一个数的二进制表达中0的个数

    int Grial(int x){int count = 0;while (x + 1){count++;x |= (x + 1);}return count;}
    
  16. 两个整数交换变量名

    void swap(int &a, int &b) {a ^= b;b ^= a;a ^= b;}
    
  17. 判断两个数是否异号

    int x = -1, y = 2;
    bool f = ((x ^ y) < 0); // true
    int x = 3, y = 2;
    bool f = ((x ^ y) < 0); // false
    
  18. 数据加密

    将需要加密的内容看做A,密钥看做B,A ^ B=加密后的内容C。而解密时只需要将C ^ 密钥B=原内容A。如果没有密钥,就不能解密!

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define KEY 0x86
    int main()
    {char p_data[16] = {"Hello World!"};char Encrypt[16]={0},Decode[16]={0};int i;for(i = 0; i < strlen(p_data); i++){Encrypt[i] = p_data[i] ^ KEY;}for(i = 0; i < strlen(Encrypt); i++){Decode[i] = Encrypt[i] ^ KEY;}printf("Initial date:  %s\n",p_data);printf("Encrypt date:  %s\n",Encrypt);printf("Decode date:  %s\n",Decode);return 0;
    }
    
    1. 求2的N次方

      1<<n
      
    2. 高低位交换

      unsigned short a = 34520;
      a = (a >> 8) | (a << 8);
      
    3. 进行二进制逆序

      unsigned short a = 34520;
      a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
      a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
      a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
      a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
      
    4. 获得int型最大最小值

      int getMaxInt(){return (1 << 31) - 1;//2147483647, 由于优先级关系,括号不可省略}int getMinInt(){return 1 << 31;//-2147483648}
      
    5. m的n次方

      //自己重写的pow()方法int pow(int m , int n){int sum = 1;while(n != 0){if(n & 1 == 1){sum *= m;}m *= m;n = n >> 1;}return sum;}
      
    6. 找出不大于N的最大的2的幂指数

      int findN(int n){n |= n >> 1;n |= n >> 2;n |= n >> 4;n |= n >> 8 // 整型一般是 32 位,上面我是假设 8 位。return (n + 1) >> 1;}
      
    7. 二分查找32位整数的前导0个数

      int nlz(unsigned x){int n;if (x == 0) return(32);n = 1;if ((x >> 16) == 0) {n = n +16; x = x <<16;}if ((x >> 24) == 0) {n = n + 8; x = x << 8;}if ((x >> 28) == 0) {n = n + 4; x = x << 4;}if ((x >> 30) == 0) {n = n + 2; x = x << 2;}n = n - (x >> 31);return n;}
      
  19. 位图的操作
    将 x 的第n位 置1,可以通过 x |= (x << n) 来实现
    set_bit(char x, int n);
    将 x 的第 n 位清0,可以通过 x &= ~(1 << n) 来实现
    clr_bit(char x, int n);
    取出 x 的第 n 位的值,可以通过 (x >> n) & 1 来实现
    get_bit(char x, int n);
    如下:

    #define clr_bit(x, n) ( (x) &= ~(1 << (n)) )
    #define set_bit(x, n) ( (x) |= (1 << (n)) )
    #define get_bit(x, n) ( ((x)>>(n)) & 1 )
    
  20. VxWorks源码中的一些位操作

    /* common macros */
    #define MSB(x)  (((x) >> 8) & 0xff)     /* most signif byte of 2-byte integer */
    #define LSB(x)  ((x) & 0xff)          /* least signif byte of 2-byte integer*/
    #define MSW(x) (((x) >> 16) & 0xffff) /* most signif word of 2-word integer */
    #define LSW(x) ((x) & 0xffff)         /* least signif byte of 2-word integer*//* swap the MSW with the LSW of a 32 bit integer */
    #define WORDSWAP(x) (MSW(x) | (LSW(x) << 16))#define LLSB(x)  ((x) & 0xff)        /* 32bit word byte/word swap macros */
    #define LNLSB(x) (((x) >> 8) & 0xff)
    #define LNMSB(x) (((x) >> 16) & 0xff)
    #define LMSB(x)  (((x) >> 24) & 0xff)
    #define LONGSWAP(x) ((LLSB(x) << 24) | \(LNLSB(x) << 16)| \(LNMSB(x) << 8) | \(LMSB(x)))
    

C语言位操作详解(全网最全)相关推荐

  1. C语言位操作详解-4.2.C语言专题第二部分-朱有鹏-专题视频课程

    C语言位操作详解-4.2.C语言专题第二部分-11753人已学习 课程介绍         本课程的目标是让大家彻底掌握C语言中位运算符及常规技巧,这些知识在嵌入式代码中用的很多,尤其是设置寄存器以操 ...

  2. 【MySQL系列】数据结构详解(全网最全)

    ​ 数据库建模,创建库表是项目开始进行编码的最后一步,库表设计的是否合理高效不仅取决于对项目业务流程的掌握程度,更要懂得数据库字段合理使用,才能设计出性能卓越易于扩展的数据模型.本文基于MySQL8, ...

  3. STM32三种BOOT启动模式详解(全网最全)

    一.三种boot启动模式 一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存.用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启动模式. ...

  4. c语言的指针详解ppt,最全的C语言指针详解.ppt

    最全的C语言指针详解.ppt 第6章 指针,6.1 指针定义与使用 6.2 指针与函数 6.3 指针与数组 6.4 指针与字符串 6.5 指针数组与多级指针 6.6 指针与动态内存分配 6.7 指针的 ...

  5. C语言指针详解(全解)—— 前篇

    关于指针的基本概念及相关运算写在了上一篇博客 C语言指针详解(初级)_Seinrich的博客-CSDN博客 本篇博客来详细的解说一下指针,深入探讨指针 一.指针与const const修饰的变量为常变 ...

  6. C语言之详解#ifdef等宏

    C语言之详解#ifdef等宏 这几个宏是为了进行条件编译.一般情况下,源程序中所有的行都参加编译.但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是&qu ...

  7. 代码检查规则:Python语言案例详解

    在之前的文章中代码检查规则:Java语言案例详解学习了Java的检查规则.我们今天将学习<代码检查规则:Python语言案例详解>,内容主要分为两个部分:Python的代码检查规则和Pyt ...

  8. C语言中可以用字符串常量来,C语言字符串详解

    原标题:C语言字符串详解 字符串是一种非常重要的数据类型,但是C语言不存在显式的字符串类型,C语言中的字符串都以字符串常量的形式出现或存储在字符数组中.同时,C 语言提供了一系列库函数来对操作字符串, ...

  9. python语言编程基础-Python语言入门详解!快速学成Python!

    原标题:Python语言入门详解!快速学成Python! 很多技能是被职场所需要的,但很可惜... 这些技能在大学中并学习不到. 大学和职场现实存在的横沟对大部分同学来说难以跨越或碰得头破血流... ...

最新文章

  1. Java 性能调优的 11 个实用技巧
  2. windows命令_用Windows恢复环境(WinRE)排除电脑故障,只需一条命令
  3. flutter text 左对齐_flutter输入框TextField中文本textAlign对齐分析篇
  4. OpenCASCADE绘制测试线束:数据交换命令之XDE 图层命令
  5. python正则表达式试题_正则表达式练习题2
  6. 计算机网络(一)计算机网络体系
  7. 业界资讯:adobe air 2.7 beta发布
  8. 教你用一条SQL搞定跨数据库查询难题
  9. 牛客练习赛30: E. 国政议事(二分匹配)
  10. springboot10 Web开发静态资源
  11. aiohttp 高并发web服务_面试官要是问你如何解决web高并发,你就这样回答
  12. 易用性软件测试用例,易用性、界面测试用例
  13. 产品配件类目税目分类_商品和服务税收分类编码表-2017版
  14. 信息论基础知识:【信息熵 不确定性】
  15. javaweb springboot餐厅点餐系统源码
  16. 【常规版本】SIS-OAS 1.53.0(2019/5/23发布)版本 --需求评审 ---模板
  17. tomcat配置优化,apr的配置详解
  18. 【Excel从头开始】-2 快速填充/快速分析
  19. index函数python什么意思_详解python中的index函数用法
  20. (蓝图)虚幻4重制俄罗斯方块(四)移动方块

热门文章

  1. opencv MSER(最大极值稳定区域)
  2. html5 在线答题活动,小程序
  3. 【数据集处理】中国地面气候资料日值数据集(V3.0)
  4. VRAR领军Google发布一大一小Pixel_VRAR123
  5. 自注意力机制-- Self-attention
  6. 为什么不应该大面积推广双壁波纹管来做排水管道?
  7. github 网速问题
  8. 如何证明夫妻已经分居2年?
  9. 程序员的自我修养——链接、装载与库 笔记(一)
  10. 云队友丨工作高效的人,拼的从来不是时间