位运算之清除特定的某几位

在C语言中,一个32位的整数能表征32种状态。那么,要将某几种特定的状态清除掉,也就是将整数对应的某几位清除掉,有固定套路吗? Absolutely yes! 固定套路如下:

FLAGS &= ~( X | Y | Z )

/*
 * 1. N      = X | Y | Z;
 * 2. M      = ~N;
 * 3. FLAGS &= M;
 */

1. 将特定的某几位对应的整数X, Y, Z使用或(|)运算组合成一个新的整数N;
2. 将新的整数N按位取反(~),得到新的整数M;
3. 以M为基,对FLAGS进行与(&)运算。

注意每一个特定的位都对应一个特定的整数。特定的整数诸如X, Y, Z是如何被定制的,以及上面的套路是如何被实施的,下面将给出一个具体的例子予以说明。

o foo.c

 1 #include <stdio.h>
 2
 3 /*
 4  *                 +---+---+---+---+---+---+---+---+
 5  *                 |8th|7th|6th|5th|4th|3rd|2nd|1st|
 6  * +---------------+---+---+---+---+---+---+---+---+
 7  * | INDEX         | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
 8  * +---------------+---+---+---+---+---+---+---+---+
 9  * | SF_ATTENTION  | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
10  * | SF_INPROGRESS | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
11  * | SF_OK         | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
12  * | SF_ERR        | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
13  * | SF_TIMEOUT    | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
14  * | SF_USYNC      | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
15  * | SF_XXX        | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
16  * | SF_YYY        | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
17  * +---------------+---+---+---+---+---+---+---+---+
18  */
19
20 #define SF_ATTENTION            001 /* entry needs servicing */
21 #define SF_INPROGRESS           002 /* sync is in progress */
22 #define SF_OK                   004 /* sync has happend successfully */
23 #define SF_ERR                  010 /* sync has happend unsuccessfully */
24 #define SF_TIMEDOUT             020 /* proc timed out in sync event */
25 #define SF_USYNC                040 /* entry is a user sync, else auto */
26
27 /**
28  * OR (2) define as (1 << N):
29  *
30  * #define SF_ATTENTION         (1 << 0)
31  * #define SF_INPROGRESS        (1 << 1)
32  * #define SF_OK                (1 << 2)
33  * #define SF_ERR               (1 << 3)
34  * #define SF_TIMEDOUT          (1 << 4)
35  * #define SF_USYNC             (1 << 5)
36  *
37  * OR (3) define as Hex:
38  *
39  * #define SF_ATTENTION         0x01
40  * #define SF_INPROGRESS        0x02
41  * #define SF_OK                0x04
42  * #define SF_ERR               0x08
43  * #define SF_TIMEDOUT          0x10
44  * #define SF_USYNC             0x20
45  */
46
47 /*
48  * If there is an int flags, we want to clear its specified bits to be zero,
49  * we will use
50  *     flags &= ~(... | ... | ...)
51  *
52  * e.g. clear the 3rd, 4th and 5th bits to be 0
53  *      flags &= ~(SF_OK | SF_ERR | SF_TIMEDOUT);
54  */
55
56 /*
57  * XXX: _NOTE is from /usr/include/sys/note.h of Solaris
58  *            note.h: interface for annotating source with info for tools
59  */
60 #ifndef _NOTE
61 #define _NOTE(s)
62 #endif
63
64 int
65 main(int argc, char *argv[])
66 {
67         unsigned char flags = SF_ATTENTION | SF_OK | SF_USYNC;
68 _NOTE(                87654321 )
69 _NOTE(                -------- )
70 _NOTE(flags == 045 == 00100101b)
71         printf("a) flags = 0%o\n", flags);
72
73 _NOTE(flags   &= ~(bit#3 | bit#4  | bit#5))
74         flags &= ~(SF_OK | SF_ERR | SF_TIMEDOUT);
75
76 _NOTE(                87654321 )
77 _NOTE(                -------- )
78 _NOTE(flags == 041 == 00100001b)
79         printf("b) flags = 0%o\n", flags);
80
81         return flags;
82 }

o 编译并运行

$ gcc -g -Wall -m32 -std=c99 -o foo foo.c

$ ./foo
a) flags = 045
b) flags = 041

$ echo $?
33

针对L67,L74这两行的位运算过程,做出如下剖析:

67         unsigned char flags = SF_ATTENTION | SF_OK | SF_USYNC;
74         flags &= ~(SF_OK | SF_ERR | SF_TIMEDOUT);

L67: flags = | SF_ATTENTION  | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
            +| SF_OK         | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
            +| SF_USYNC      | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
           =                 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 1 |

L74:  (SF_OK | SF_ERR | SF_TIMEDOUT)
           = | SF_OK         | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
            +| SF_ERR        | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
            +| SF_TIMEOUT    | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
           =                 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |

     ~(SF_OK | SF_ERR | SF_TIMEDOUT)
           =                 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |

     flags & ~(SF_OK | SF_ERR | SF_TIMEDOUT)
           =                 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 1 |
                            &| 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
           =                 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
           = | SF_ATTENTION  | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
            +| SF_USYNC      | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |

由此可见,构造一个某几个特定的位为0但是其他所有位都为1的整数是整个运算过程的关键所在

那么,这一固定套路有什么实际的用处呢? 当然用处是大大的,只要涉及到状态机的切换,就不可避免地要将某一个或者某几个特定的状态清除掉。例如: linux-4.9.16/kernel/printk/printk.c#2557

总结:

清除整数的某几位的套路 : FLAGS &= ~( X | Y | Z )
设置整数的某几位的套路 : FLAGS |=  ( X | Y | Z )
时间: 2024-10-07 01:01:50

位运算之清除特定的某几位的相关文章

Java位运算在程序设计中的使用:位掩码(BitMask)

在Java中,位运算符有很多,例如与(&).非(~).或(|).异或(^).移位(<<和>>)等.这些运算符在日常编码中很少会用到. 在下面的一个例子中,会用到位掩码(BitMask),其中包含大量的位运算.不只是在Java中,其他编写语言中也是可以使用的. 例如,在一个系统中,用户一般有查询(Select).新增(Insert).修改(Update).删除(Selete)四种权限,四种权限有多种组合方式,也就是有16中不同的权限状态(2的4次方). 一般情况下会想到用四个

用位运算实现四则运算之加减乘除(用位运算求一个数的1/3) via Hackbuteer1

转自:http://blog.csdn.net/hackbuteer1/article/details/7390093 ^: 按位异或:&:按位与: | :按位或 计算机系统中,数值一律用补码来表示:因为补码可以使符号位和数值位统一处理,同时可以使减法按照加法来处理. 对补码做简单介绍:数值编码分为原码,反码,补码,符号位均为0正1负. 原码 -> 补码: 数值位取反加1 补码 -> 原码: 对该补码的数值位继续 取反加1 补码 的绝对值(称为真值):正数的真值就是本身,负数的真值是各

【C语言位运算的应用】如何按bit位翻转一个无符号整型

其实现思路如下: 将目标数值进行末尾比特位摘取,将所摘取的bit位放到一个相同类型的末尾,目标数值bit位右移,相同类型bit位左移. C语言的位运算符:     实现代码如下: #include <stdio.h>//按位翻转一个无符号整形 unsigned int reverse_bit(unsigned int value) { unsigned int num = 0; int i = 0; for (i = 1; i < 32; i++) { num += value &

位运算取第一个非0的位 r &amp; (~(r-1))

Single Number III Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once. For example: Given nums = [1, 2, 1, 3, 2, 5], return [3, 5]

位运算(&amp;amp;、|、^)与逻辑运算(&amp;amp;&amp;amp;、 ||)差别

刚无意在一篇文章中看到了位运算(&.|)和逻辑运算(&&.||)的介绍.想起了自己薄弱的基础知识.于是百度了几把总结了下. 首先从概念上区分下,位运算是将运算符两边的数字换算成二进制(例:0000010001)后比較同样位置上的0.1进行运算的.逻辑运算即比較运算符两边的逻辑值(true或false).概念比較抽象.下边借助实际样例比較下. 位运算 先将每一个数转换成二进制.然后进行.位或(|)表示相相应的每位至少有一个为1.则结果为1,仅仅有两个都为0.结果才为0.位与(&

位运算(&amp;、|、^)与逻辑运算(&amp;&amp;、 ||)区别

刚无意在一篇文章中看到了位运算(&.|)和逻辑运算(&&.||)的介绍,想起了自己薄弱的基础知识,于是百度了几把总结了下. 首先从概念上区分下,位运算是将运算符两边的数字换算成二进制(例:0000010001)后比较相同位置上的0.1进行运算的.逻辑运算即比较运算符两边的逻辑值(true或false).概念比较抽象,下边借助实际例子比较下. 位运算 先将每个数转换成二进制,然后进行,位或(|)表示相对应的每位至少有一个为1,则结果为1,只有两个都为0,结果才为0.位与(&)

Java中的二进制及基本的位运算

Java中的二进制及基本的位运算 二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.它的基数为2,进位规则是"逢二进一",借位规则是"借一当二",由18世纪德国数理哲学大师莱布尼兹发现.当前的计算机系统使用的基本上是二进制系统,数据在计算机中主要是以补码的形式存储的.计算机中的二进制则是一个非常微小的开关,用"开"来表示1,"关"来表示0. 那么Java中的二进制又是怎么样的呢?让我们一起来揭开它神

位运算总结&amp;拾遗

JavaScript 位运算总结&拾遗 最近补充了一些位运算的知识,深感位运算的博大精深,此文作为这个系列的总结篇,在此回顾下所学的位运算知识和应用,同时也补充下前文中没有提到的一些位运算知识. 把一个数变为大于等于该数的最小的2的幂 一个数为2的幂,那么该数的二进制码只有最高位是1. 根据这个性质,我们来举个栗子,比如有数字10,转为二进制码后为: 1 0 1 0 我们只需把 0 bit的位置全部用1填充,然后再把该二进制码加1就ok了.而x | (x + 1)正好可以把最右边的0置为1,可是

算法学习 - 递归与非递归,位运算与乘除法速度比较

递归调用非递归调用 运行时间比较 结论 位运算与乘除法 结论 递归调用/非递归调用 我们都知道,很多算法,都是用递归实现的.当然它们同时也是可以用非递归来实现. 一般我们在对二叉树进行遍历的时候,还有求斐波那契数的时候,递归是非常简单的.代码容易懂,好实现. 但是递归的时候,有一个问题,就是需要压栈.为什么要压栈呢?因为当我在函数内部调用自身的时候,要中断当前的操作继续跳转到下一次的实现,而当前运行的状态要保存起来.所以就把当前状态进行压栈,等到运行到递归条件结束的时候,再弹栈. 所以递归就是需