c语言里下有丰富的位运算,它们能很好的帮助我们对位进行操作

~、<<、>>、&、^、|、&=、^=、|=、<<=、>>=

这些位运算里,常用的有就是:与运算(&),位移运算(<< >>),还有或运算和异或运算(|,^)

许多c语言初学者可能只知道这些运算的意思,但是很少在实际编程中去用到它,其次是即便已经参与工作的程序员来说,也相对的较少用到它们,但是这些运算在Linux内核里非常容易见到,还有底层开发人员(单片机和嵌入式),因为底层开发人员需要对bit位进行转换(高低电平)

Linux内核里特别喜欢使用大量的位运算,这样有效加速运算,其次也显得很酷

如你想要做一个乘法,且这个数是2的倍数那么你可以这样做:

a = a * 64;

用位运算:

a = a << 6;

这样CPU就无需去调用ALU运算单元来执行对应的加法指令集,这样CPU只需要把a这个变量上的bit位向左移动6位就可以了。

几乎可以节约很大程度的计算,因为乘法需要累加器,而且不是一次就能做完的,详细可以参考我写的这篇文章:加减乘除实现

而位移就不一样了,只是简单的取出变量的bit,然后移动一下,在放回去,效率可以质上的提升。

这里解释一下,为什么2的倍数可以以位移的方式来完成

bit是以2的次方为单位的,这点学过计算机原理的应该都很清楚,从低位到高位每一次都是一个次方的叠加

而从低位到高位上面的64,刚好位移6次可以得到,所以把a变量的原bit,左移6位,随着次方的叠加,就相当于乘上64

有兴趣的小伙伴可以试试,把a的值转化为二进制数,然后右移6位,看一下算出的值是否与乘法结果后的值一样。

当我们写程序时,想要传递一些参数时

如有这样的一个场景,我们编写了一个程序,这个是一个窗体组件,是以接口形式提供给用户的,当用户调用时不可避免的需要传递一些参数

如:

组件的样式,标题,宽度和高度,x和y坐标等

就拿最简单的样式吧,样式可以有多种,如,凹凸,平滑,无边框,且这些样式允许同时存在,我们不可能傻乎乎的写很多样式宏吧?

类似这种:

#define 凹凸 0#define 平滑 1#define 无边框 2#define 凹凸加平滑 3#define 凹凸加无边框 4...

类似这种的,还有使用大量参数的,这种写法我也见过,我觉得这样非常浪费内存空间,以及开发效率,和代码质量

这些样式都是很简单的1-2-3这样的数字编号组成的,非常小,以至于8个bit位就能表示

如果我们使用一个int类型,把低8位用来存储凹凸,中间八位用来存储平滑,高八位用来存储无边框

然后在接口里取出int类型的低八位和中间八位还有高八位,然后判断是否有值就可以了,很简单

int是32位(随编译位数),这样的情况下还有八位可以当作保留,那么这样的好处就在于可以有效处理多个样式。

当然,这只是例子,你可以用在其它问题上。

下面给大家说一下,如何在实际运用中去运用位运算来解决上面这个问题

使用按位"&"方式来对位进行操作

复习“&”

1与0为0;1与1为1;0与0为0。

因为按位与的特点,我们可以通过按位一些不同的bit来达到赋予值的目的

如10的二进制是1010

当int a变量的值是0

那么

1010&00000000 00000000 00000000 000000000

则是

00000000 00000000 00000000 00001010

是不是发现好多0,这是因为int a是32位的,在底层运算时,完整的运算bit就是这样,且是从低到高位依次运算

同时当我们c语言赋予值的时,其实编译器会把它看成一个32位整型如:

int a = 0;

a = a&10;

位运算:

00000000 00000000 00000000 00001010&00000000 00000000 00000000 000000000

这里10 c语言编译器会通过一些类型推举,认为它是32位整数,然后赋值给int a

那么假如int a是1234,二进制是:00000000 00000000 00001001 1010010

我们a&10 就是:00000000 00000000 00000000 00001010&00000000 00000000 00001001 1010010

因为与的特性,所以只有最低的八位发生了变化,但是如果我们要是想把10这个值赋到高八位怎么办?

答:位移运算

通过位移把10的二进制数左移位就可以了

10左移八位是:00000000 00000000 00001010 00000000

然后与运算一下就可以了

实战一下吧~

#define INPUT_LOW_8(SOUR,VAR)({\SOUR|=VAR&0x00FF;\
})
#define INPUT_HEIGHT_8(SOUR,VAR)({\SOUR|=(VAR<<8)&0x0FF00;\
})#define OUTPUT_LOW_8(SOUR) (SOUR&0X00FF)
#define OUTPUT_HEIGHT_8(SOUR) (SOUR&0xFF00)>>8

上面这个是我在一个项目里写的,这里大家可以根据我们上面说的原理自己先分析一下

先说第一个

#define INPUT_LOW_8(SOUR,VAR)({\SOUR|=VAR&0x00FF;\
})

这里SOUR就是变量,而VAR就是要写入低八位的值

SOUR|=VAR&0xFF;

这里用或运算是因为为了不覆盖旧值,比如高位已经有值了,如果你用=号的话那么高位会被覆盖掉

如SOUR=VAR

即便VAR是0001这样的小值,被c语言会翻译成0000 0000 0000 0000 0000 0001这样的32位整型,然后赋予给SOUR

但是如果是|运算就不一样了,这里给大家顺便说一下或运算的特点:

1001|101=1101

有一时为一,都是0时为0,不相同时为一

在复习一下&和|

&: 二进制“与”(都为1时,结果是1,否则是0。),比如:1010 & 1011 = 1010,1010 & 1000 = 1000。

|: 二进制“或”(有1时,结果是1,都是0时,结果为0。),比如:1010 | 1011 = 1011,1010 | 1000 = 1010。

这两个运算符特点很相似,一般使用时容易混淆

后面的VAR&0xFF是因为假如传递进来的是一个大值,比如1234这样的值,我们只要低八位,所以&上0xFF,就可以取到低八位的值,然后写进去,这样可以防止一些额外的bit运算

如果你能确定VAR的值会小于8位,那么你可以省略掉后面的&0xFF

#define INPUT_HEIGHT_8(SOUR,VAR)({\SOUR|=(VAR<<8)&0x0FF00;\
})

这一个其实很简单,结合前面说的,左移8位,c语言会把它堪称32位整型处理,后面的&0x0FF00;也是左移了八位的FF,跟上面一样的原理。

这里说一下

一个F最大能表示15,而15的bit是1111,也就是说一个F能运算四个bit位,FF刚好8位

一般都会简写成0xFFFFFFF,实则上c语言会看成0x00FFFFFFFF

#define OUTPUT_LOW_8(SOUR) (SOUR&0X00FF)
#define OUTPUT_HEIGHT_8(SOUR) (SOUR&0xFF00)>>8

如果前面所说的你已经理解了,那么这段代码可以很轻松的理解

SOUR&0xFF,这里是将SOUR的值与低位运算一下,忽略高位的运算

而SOUR&0xFF00>>8 则是将高位的运算结果(同时也忽略其它位,只运算高16位)左移八位,因为c语言是从低位开始运算的,如果左移八位则:

假如SOUR&0xFF00的结果是0000 1111 0000 如果不左移八位,那么结果就是:240,这跟原本存入进去的值就不一样了,所以要左移八位到低位上去

demo:

#include <stdio.h>#define INPUT_LOW_8(SOUR,VAR)({\SOUR|=VAR&0x00FF;\
})
#define INPUT_HEIGHT_8(SOUR,VAR)({\SOUR|=(VAR<<8)&0x0FF00;\
})#define OUTPUT_LOW_8(SOUR) (SOUR&0X00FF)
#define OUTPUT_HEIGHT_8(SOUR) (SOUR&0xFF00)>>8int main(){int a=0;INPUT_LOW_8(a,30);INPUT_HEIGHT_8(a,20);printf("%d %d",OUTPUT_LOW_8(a),OUTPUT_HEIGHT_8(a));
}

结果:

30 20

但是这样的运算是有一个问题的

比如我们修改一下main函数:

int main(){int a=1234;INPUT_LOW_8(a,30);INPUT_HEIGHT_8(a,20);printf("%d %d",OUTPUT_LOW_8(a),OUTPUT_HEIGHT_8(a));
}

这里将a初始化为1234:

222 20

问题来了,很明显 在获取低位的时出错啦

这里经过我调试分析得出的结果:

1234的bit=10011010010

30的bit=11110

当写入到低位时,原本的1234就会变成:10011011110

可以看到低位已经被赋予了30的bit

那么当10011011110 &11111111(0xFF)

10011011110

&

000011111111

=

000011011110

可以看到,是1的都保留了下来,因为30的bit 5个bit就能表示,但是恰巧在&上0xFF的时,其它位上也有值

也是1,导致被保留了下来

所以解决办法就是,在赋予LOW的时用=号,或者给变量初始化0都可以

这里在介绍一个如何获取每个变量的bit?

这里是我在项目里写的一个宏:

#define GET_BIT(NUM,I) (bool)(NUM&(1<<I))

NUM代表变量,I代表要获取的第几位的bit

(NUM&(1<<I))

这里其实也是用了位移

比如:

NUM=5(101)

1<<2,就是将1这个值左移两位

而1的bit位是0001,左移两位0100

然后5(101)&(100)=(100)

就取到了这个位上有没有值,但是不会返回1,返回结果是与运算后的结果100(4)

最前面的bool完美解决这个问题

因为在布尔值里,1代表TRUE,0代表FALSE

所以转化为布尔值后c语言语言会帮我们进行一个类型转换,当结果非0时,c语言会转化为1,0时就会返回0

在c语言里0代表假,非0代表真

c语言位操作的高级应用相关推荐

  1. 嵌入式c语言移植,嵌入式C语言位操作的移植方法

    单片机的应用越来越广泛,种类也越来越多.由于嵌入式C语言可读性强.移植性好,与汇编语言相比大大减轻了软件工程师的劳动强度,因而越来越多的单片机工程师开始使用C语言编程.但C语言的可移植性仅限于与硬件无 ...

  2. 嵌入式c语言移植,嵌入式c语言位操作的移植与优化.doc

    嵌入式C语言位操作的移植与优化 导语:?由于嵌入式C语言可读性强.移植性好,与汇编语言相比大大减轻了软件工程师的劳动强度,因而越来越多的单片机工程师开始使用C语言编程. 引言 单片机的应用越来越广泛, ...

  3. C语言指针的高级操作

    C语言指针的高级操作 指针  指针 在上篇博客中我介绍了C语言指针的最基本操作,那么我在这篇博客中会介绍一下C语言指针的一些骚操作. 指向指针的指针 这名字乍一听有点拗口,再次一听就更加拗口了.先看定 ...

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

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

  5. R语言学习笔记——高级篇:第十四章-主成分分析和因子分析

    R语言 R语言学习笔记--高级篇:第十四章-主成分分析和因子分析 文章目录 R语言 前言 一.R中的主成分和因子分析 二.主成分分析 2.1.判断主成分的个数 2.2.提取主成分 2.3.主成分旋转 ...

  6. C语言位操作中指定的某一位数置0、置1、取反

    C语言位操作中指定的某一位数置0.置1.取反 一.指定的某一位数置1 宏 #define setbit(x,y)  x|=(1<<y) 二.指定的某一位数置0 宏  #define clr ...

  7. C++语言的一些高级特性

    摘要:本文结合自身的工作经验和学习心得,对C++语言的一些高级特性,做了简单介绍:对一些常见的误解,做了解释澄清:对比较容易犯错的地方,做了归纳总结:希望借此能增进大家对C++语言了解,减少编程出错, ...

  8. c语言置字节某一位为0,C语言位操作中指定的某一位数置0、置1、取反

    C语言位操作中指定的某一位数置0.置1.取反 一.指定的某一位数置1 宏 #define setbit(x,y)  x|=(1< 二.指定的某一位数置0 宏  #define clrbit(x, ...

  9. 易语言高级表格写入MYSQL_易语言数据库与高级表格思路分析.doc

    易语言数据库与高级表格思路分析 篇一:易语言数据库操作指南 易语言数据库操作指南 一.易语言操作Access数据库 前面我们已经对比分析过易语言所支持的几种常见数据库,在这几种数据库中,我们先来学习一 ...

最新文章

  1. IIS6.0 日期格式问题
  2. 如何查看开发者账号何时到期
  3. 机器学习实战笔记(Python实现)-01-机器学习实战
  4. 爱拉托逊斯方法以及素数判断
  5. 个人博客作业第三周--必应词典分析
  6. STL学习之一(栈(statck))
  7. 为什么不能在中断上半部休眠?
  8. 如何使用matlab的siso,利用Matlab内建程式SISODesignTool完成系统分析(Matlab61)开启.PDF...
  9. 自适应网站设计对百度友好的关键:添加applicable-device标签(转)
  10. 程序阅读理解题目(高中语文版,附答案)
  11. kafka学习_kafka学习(第一章 初识kafka)
  12. 《我也能做CTO之程序员职业规划》之三:曲线定律
  13. Linux中如何让进程在后台运行
  14. 数据挖掘概念与技术(原书第三版)范明 孟小峰译-----第三章课后习题答案
  15. 浅谈IDEA Scratch files万能的临时文件功能
  16. STM32应用(三)一阶卡尔曼滤波原理和ADC读取红外测距模块的数值
  17. word转pdf转换器免费版是一款专业将word文档转换成pdf文件的软件,完美支持在线word转换成pdf,可将office word文档doc、docx、wps格式转换成PDF格式。word转pd
  18. 组态王 6.55 启停plc_成套设备PLC编程深圳PLC编程
  19. fullpage插件的使用
  20. 阿里云 ECS windows服务器创建+部署+域名

热门文章

  1. python 中文乱码_python 解决cv2绘制中文乱码问题
  2. 用递归和非递归实现斐波那契数列
  3. python和matlab的区别_我为什么选择Python,不是Matlab和R语言呢?
  4. studio创建java工程_Android studio从头一步步创建Java项目
  5. 查看uboot变量地址_华为FIT AP通过Uboot切换FAT模式
  6. 不是华为手机可以刷鸿蒙系统,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可 !【手机吧】_百度贴吧...
  7. 静态内部类实现单例_为什么用枚举类来实现单例模式越来越流行?
  8. 03、了解自动配置原理笔记
  9. c语言libjpeg处理图像,解决使用 libjpeg 保存图片时因磁盘写入失败导致程序退出的有关问题...
  10. p坚持csma协议 仿真‘_巧家快速推进移民生产安置人口界定和协议签订工作