位运算之清除特定的某几位
Posted veli
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了位运算之清除特定的某几位相关的知识,希望对你有一定的参考价值。
在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 )
APPENDIX: Get the Nth bit of an Integer
hd$ cat /tmp/a.py #!/usr/bin/python3 def get_bit(n, m): return ((n & (1 << m)) >> m) for i in [0, 1, 2, 4, 8, 16, 32, 64]: for j in range(8): print("int = %2d, pos = %d, bit=%d" % (i, j+1, get_bit(i, j))) print()
hd$ /tmp/a.py int = 0, pos = 1, bit=0 int = 0, pos = 2, bit=0 int = 0, pos = 3, bit=0 int = 0, pos = 4, bit=0 int = 0, pos = 5, bit=0 int = 0, pos = 6, bit=0 int = 0, pos = 7, bit=0 int = 0, pos = 8, bit=0 int = 1, pos = 1, bit=1 int = 1, pos = 2, bit=0 int = 1, pos = 3, bit=0 int = 1, pos = 4, bit=0 int = 1, pos = 5, bit=0 int = 1, pos = 6, bit=0 int = 1, pos = 7, bit=0 int = 1, pos = 8, bit=0 int = 2, pos = 1, bit=0 int = 2, pos = 2, bit=1 int = 2, pos = 3, bit=0 int = 2, pos = 4, bit=0 int = 2, pos = 5, bit=0 int = 2, pos = 6, bit=0 int = 2, pos = 7, bit=0 int = 2, pos = 8, bit=0 int = 4, pos = 1, bit=0 int = 4, pos = 2, bit=0 int = 4, pos = 3, bit=1 int = 4, pos = 4, bit=0 int = 4, pos = 5, bit=0 int = 4, pos = 6, bit=0 int = 4, pos = 7, bit=0 int = 4, pos = 8, bit=0 int = 8, pos = 1, bit=0 int = 8, pos = 2, bit=0 int = 8, pos = 3, bit=0 int = 8, pos = 4, bit=1 int = 8, pos = 5, bit=0 int = 8, pos = 6, bit=0 int = 8, pos = 7, bit=0 int = 8, pos = 8, bit=0 int = 16, pos = 1, bit=0 int = 16, pos = 2, bit=0 int = 16, pos = 3, bit=0 int = 16, pos = 4, bit=0 int = 16, pos = 5, bit=1 int = 16, pos = 6, bit=0 int = 16, pos = 7, bit=0 int = 16, pos = 8, bit=0 int = 32, pos = 1, bit=0 int = 32, pos = 2, bit=0 int = 32, pos = 3, bit=0 int = 32, pos = 4, bit=0 int = 32, pos = 5, bit=0 int = 32, pos = 6, bit=1 int = 32, pos = 7, bit=0 int = 32, pos = 8, bit=0 int = 64, pos = 1, bit=0 int = 64, pos = 2, bit=0 int = 64, pos = 3, bit=0 int = 64, pos = 4, bit=0 int = 64, pos = 5, bit=0 int = 64, pos = 6, bit=0 int = 64, pos = 7, bit=1 int = 64, pos = 8, bit=0
以上是关于位运算之清除特定的某几位的主要内容,如果未能解决你的问题,请参考以下文章