带参数的宏定义实战篇
前言:最近在看一位叫朱有鹏大神的视频,讲的甚好。应此,我的感悟也因此被激发,准备针对朱老师将的内容,结合自己的理解,写一个系列的笔记博客~~大家可以去www.zhulaoshi.org观看视频~~
要想理解此文,需要熟悉位操作的方法,您可能需要先阅读这篇文章—:《arm学习——有关位操作的总结》
我们用带参数的宏定义,来实现一些有用的位操作。
1、直接用宏来置位、复位(最右边为第0位)
#define SET(x,n) ( (x) | ( 1U << (n) ) )
#define CLEAR(x,n) ( (x) & ~( 1U << (n) ) )
2、截取变量的部分连续位。例如:变量0x88, 也就是10001000b,若截取第7~5位,则值
为:100b = 4
我们给出开头:
#define GETBITS(x, high, low) 宏体
这其实是想读取某个变量的连续几位,在《arm学习——有关位操作的总结》提到过,“读”在乎的是
不变的位。我们可以利用&的特性——任何数(0或1)和1与(&),结果不变;和0与(&)结果为0.
那么我们想读x变量的取7~5位,就可以这么写x = (x & (7<<5))>> 5
测试程序:
int x = 0xe0;//0b1110_0000
x = (x & (7<<5))>> 5;
printf("x = %d\n",x);//结果为7,及0b111
那么说明结果是对的,那么接下来。我们如何用带参数的宏定义实现呢?实现的关键是
实现随意制造连续的1.如上题中,需截取7~5位及3位,于是我们直接给出7,因为7用二进制
表示就是111.
首先想到的方法是寻找位数3和7的关系,容易得出2的3次方减1等于7,进而得到
“2的n(位数)次方减1 = 一个进制数”,如需要4个并排的1:1111,那么就是2的4(位数)次方减1= 15;
这样确实可以确立关系,但是未免也过于复杂。
其次,想到直接通过平移的方式,先构造出全为1的数,然后通过左移和右移动把不需要的1移
出去。但是需要注意的是,对于一个无符号数来说,不管是左移还是右移都是补0的,而不是补1.
我们就利用这个特性来实现:假如我想得到3个1:111,那么我们一个全1的数左移3位,那么
我们得到一个左边3位为0,其余位都为1的数...1111_1000,接着将这个数按位取反,我们就得到
了3个1:111。真是巧妙。通过 ~(~(0U)<<(high-low+1))就能得到想要的“并排的1”。
(x & (7<<5))>> 5,接着将其带入到这个特殊式,让其成为一般式即可(该加上的括号还得加):
((x & (~(~(0U)<<(high-low+1))<<low))>> low) ,这里其实还可以去掉一个括号
((x & ~(~(0U)<<(high-low+1))<<low)>> low),原因就是~优先级高于&,其实我还是喜欢加上这个括号,
更便于理解。
最后呢,还是在把x high low之类的再扩上一层,得到最终的结果
#define GETBITS(x, high, low) (((x) & (~(~(0U)<<((high)-(low)+1))<<(low)))>> (low))
程序验证:
int x = 0xfc;
x = GETBITS(x,7,5);
printf("x = %d\n",x);
总结:有了这个几个宏,以后ARM中操作寄存器就非常方便了~~
#define SET(x,n) ( (x) | ( 1U << (n) ) ) //设置某个寄存器的某位为1(最右边为第0位)
#define CLEAR(x,n) ( (x) & ~( 1U << (n) ) ) //设置某个寄存器的某位为0(最右边为第0位)
#define GETBITS(x, high, low) (((x) & (~(~(0U)<<((high)-(low)+1))<<(low)))>> (low)) //读取某个寄存器的 high~low(最右边为第0位)
//------分析过程-----朱老师笔记原文(稍有修改)-------
看到这么复杂的宏解析方法也很重要:首先找到配对的括号,一步步逐层分析:
//-------该部分摘抄自朱老师大课堂笔记,稍有改动----
复杂宏怎么分析:
((x & ~(~(0U)<<(high-low+1))<<(low))>> (low))
第一步,先分清楚这个复杂宏分为几部分:2部分
(x & ~(~(0U)<<(high-low+1))<<(low)) >> (low)
第二步,继续解析剩下的:又分为2部分
x & ~(~(0U)<<(high-low+1))<<(low)
第三步,继续分析剩下的:
~(~(0U)<<(high-low+1))<<(low)
这个分析时要搞清楚第2坨到底应该先左边取反再右边<<还是先右边<<再左边取反。
解法:第一,查C语言优先级表;第二,自己实际写个代码测试。
说明这个式子应该是~(~(0U)<<(high-low+1))<<(low) ,这就又分为2部分了
//------------------------------------------------
作者:宋桓公
出处:http://www.cnblogs.com/douzi2/
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。