首页 > 代码库 > 位运算之清除特定的某几位

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

在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 )

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