带参数的宏定义实战篇

前言:最近在看一位叫朱有鹏大神的视频,讲的甚好。应此,我的感悟也因此被激发,准备针对朱老师将的内容,结合自己的理解,写一个系列的笔记博客~~大家可以去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部分了

//------------------------------------------------

posted @ 2015-11-03 19:19  宋桓公  阅读(1067)  评论(0编辑  收藏  举报