c语言位操作的高级应用
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语言位操作的高级应用相关推荐
- 嵌入式c语言移植,嵌入式C语言位操作的移植方法
单片机的应用越来越广泛,种类也越来越多.由于嵌入式C语言可读性强.移植性好,与汇编语言相比大大减轻了软件工程师的劳动强度,因而越来越多的单片机工程师开始使用C语言编程.但C语言的可移植性仅限于与硬件无 ...
- 嵌入式c语言移植,嵌入式c语言位操作的移植与优化.doc
嵌入式C语言位操作的移植与优化 导语:?由于嵌入式C语言可读性强.移植性好,与汇编语言相比大大减轻了软件工程师的劳动强度,因而越来越多的单片机工程师开始使用C语言编程. 引言 单片机的应用越来越广泛, ...
- C语言指针的高级操作
C语言指针的高级操作 指针 指针 在上篇博客中我介绍了C语言指针的最基本操作,那么我在这篇博客中会介绍一下C语言指针的一些骚操作. 指向指针的指针 这名字乍一听有点拗口,再次一听就更加拗口了.先看定 ...
- C语言位操作详解-4.2.C语言专题第二部分-朱有鹏-专题视频课程
C语言位操作详解-4.2.C语言专题第二部分-11753人已学习 课程介绍 本课程的目标是让大家彻底掌握C语言中位运算符及常规技巧,这些知识在嵌入式代码中用的很多,尤其是设置寄存器以操 ...
- R语言学习笔记——高级篇:第十四章-主成分分析和因子分析
R语言 R语言学习笔记--高级篇:第十四章-主成分分析和因子分析 文章目录 R语言 前言 一.R中的主成分和因子分析 二.主成分分析 2.1.判断主成分的个数 2.2.提取主成分 2.3.主成分旋转 ...
- C语言位操作中指定的某一位数置0、置1、取反
C语言位操作中指定的某一位数置0.置1.取反 一.指定的某一位数置1 宏 #define setbit(x,y) x|=(1<<y) 二.指定的某一位数置0 宏 #define clr ...
- C++语言的一些高级特性
摘要:本文结合自身的工作经验和学习心得,对C++语言的一些高级特性,做了简单介绍:对一些常见的误解,做了解释澄清:对比较容易犯错的地方,做了归纳总结:希望借此能增进大家对C++语言了解,减少编程出错, ...
- c语言置字节某一位为0,C语言位操作中指定的某一位数置0、置1、取反
C语言位操作中指定的某一位数置0.置1.取反 一.指定的某一位数置1 宏 #define setbit(x,y) x|=(1< 二.指定的某一位数置0 宏 #define clrbit(x, ...
- 易语言高级表格写入MYSQL_易语言数据库与高级表格思路分析.doc
易语言数据库与高级表格思路分析 篇一:易语言数据库操作指南 易语言数据库操作指南 一.易语言操作Access数据库 前面我们已经对比分析过易语言所支持的几种常见数据库,在这几种数据库中,我们先来学习一 ...
最新文章
- IIS6.0 日期格式问题
- 如何查看开发者账号何时到期
- 机器学习实战笔记(Python实现)-01-机器学习实战
- 爱拉托逊斯方法以及素数判断
- 个人博客作业第三周--必应词典分析
- STL学习之一(栈(statck))
- 为什么不能在中断上半部休眠?
- 如何使用matlab的siso,利用Matlab内建程式SISODesignTool完成系统分析(Matlab61)开启.PDF...
- 自适应网站设计对百度友好的关键:添加applicable-device标签(转)
- 程序阅读理解题目(高中语文版,附答案)
- kafka学习_kafka学习(第一章 初识kafka)
- 《我也能做CTO之程序员职业规划》之三:曲线定律
- Linux中如何让进程在后台运行
- 数据挖掘概念与技术(原书第三版)范明 孟小峰译-----第三章课后习题答案
- 浅谈IDEA Scratch files万能的临时文件功能
- STM32应用(三)一阶卡尔曼滤波原理和ADC读取红外测距模块的数值
- word转pdf转换器免费版是一款专业将word文档转换成pdf文件的软件,完美支持在线word转换成pdf,可将office word文档doc、docx、wps格式转换成PDF格式。word转pd
- 组态王 6.55 启停plc_成套设备PLC编程深圳PLC编程
- fullpage插件的使用
- 阿里云 ECS windows服务器创建+部署+域名
热门文章
- python 中文乱码_python 解决cv2绘制中文乱码问题
- 用递归和非递归实现斐波那契数列
- python和matlab的区别_我为什么选择Python,不是Matlab和R语言呢?
- studio创建java工程_Android studio从头一步步创建Java项目
- 查看uboot变量地址_华为FIT AP通过Uboot切换FAT模式
- 不是华为手机可以刷鸿蒙系统,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可
!【手机吧】_百度贴吧...
- 静态内部类实现单例_为什么用枚举类来实现单例模式越来越流行?
- 03、了解自动配置原理笔记
- c语言libjpeg处理图像,解决使用 libjpeg 保存图片时因磁盘写入失败导致程序退出的有关问题...
- p坚持csma协议 仿真‘_巧家快速推进移民生产安置人口界定和协议签订工作