在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
