简体   繁体   English

C:位字段和按位运算符

[英]C: Bit fields and bitwise operators

My professor has assigned us some homework which uses bit fields, and has given us three macros 我的教授为我们分配了一些使用位字段的作业,并给了我们三个宏

# define SETBIT(A, k) { A[k >> 3] |= (01 << (k & 07)); }
# define CLRBIT(A, k) { A[k >> 3] &= ~(01 << (k & 07)); }
# define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)

which we're not required/expected to fully understand, only to use to complete the assignment. 我们不需要/也不希望完全了解这些内容,而只是用来完成任务。 Each of these macros takes an unsigned int and and index (0-8) and sets/gets/clears a bit at that index. 这些宏中的每个宏都采用unsigned int和and索引(0-8),并在该索引处设置/获取/清除位。 I get this and I get how to use it. 我得到了,我得到了如何使用它。

What I want to know is exactly what each of these macros does. 我想知道的是这些宏的每个功能。 Can somebody explain this to me like I'm five? 像我五岁时,有人可以向我解释吗?

What the macros do 宏做什么

Ignoring the problems outlined in the next section, the macros treat an array of an integral type as an array of 8-bit values, and when asked to work on bit k , processes the k%8 th bit of the k/8 th element of the array. 忽略下一节中概述的问题,宏将整数类型的数组视为8位值的数组,并且当要求对位k处理时,处理 k/8 元素的 k%8 位的数组。

However, rather than using k % 8 or k / 8 , it uses shifts and masking. 但是,它没有使用k % 8k / 8 ,而是使用了移位和屏蔽。

# define SETBIT(A, k) { A[k >> 3] |= (01 << (k & 07)); }
# define CLRBIT(A, k) { A[k >> 3] &= ~(01 << (k & 07)); }
# define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
  • k >> 3 shifts a value right by 3 bit positions, effectively dividing by 8. k >> 3将值右移3位,有效除以8。
  • k & 07 extracts the 3 least significant bits (because 07 octal or 7 decimal is 111 in binary), ignoring the rest. k & 07提取3个最低有效位(因为07八进制或7十进制为二进制111 ),忽略其余部分。
  • The 01 << (k & 07) shifts the value 1 left by 0..7 bits depending on the value of k & 07 , producing one of the binary values: 01 << (k & 07)移动通过视的值0..7位向左值1 k & 07 ,产生的二进制值中的一个:

     0000 0001 0000 0010 0000 0100 0000 1000 0001 0000 0010 0000 0100 0000 1000 0000 

    Formally, it is actually an int value, and hence probably has 32 bits, but the high order bits are all zeros. 形式上,它实际上是一个int值,因此可能具有32位,但是高阶位全为零。

  • The ~ operator converts each 0 bit into a 1 and each 1 bit into a 0. ~运算符将每个0位转换为1,并将每个1位转换为0。

  • The & operator combines two values, yielding a 1 bit where both bits are 1 and a 0 where either or both bits are 0. &运算符组合两个值,产生一个1位(两个位均为1)和0(其中一个或两个位均为0)。
  • The | | operator combines two values, yielding a 0 bit where both bits are 0 and a 1 where either or both bits are 1. 运算符组合两个值,产生两个位均为0的0位和一个或两个位均为1的1。
  • The assignment operators |= and &= apply the operand on the RHS to the variable on the LHS. 赋值运算符|=&=将RHS上的操作数应用于LHS上的变量。 The notation a |= b; 符号a |= b; is equivalent to a = a | b; 等价于a = a | b; a = a | b; except that a is evaluated just once. 除了a仅被评估一次。 This detail doesn't matter here; 这个细节在这里无关紧要; it matters intensely if there is an increment or something similar in the expression a . 如果表达式a存在增量或类似的内容,这将a

Putting it all together: 放在一起:

  • SETBIT sets the k th bit (meaning sets it to 1) in the array of 8-bit values represented by A . SETBIT设置由A表示的8位值数组中的 k位(意味着将其设置为1)。
  • CLRBIT resets the k th bit (meaning sets it to 0) in the array of 8-bit values represented by A . CLRBIT重置由A表示的8位值数组中的 k位(意味着将其设置为0)。
  • GETBIT finds the value in the k th bit in the array of 8-bit values represented by A , and returns it as either 0 or 1 — that's what the final >> (k & 07) does. GETBIT发现在值k 由下式表示的8位值的阵列中位A ,并将其返回作为要么01 -这就是最终>> (k & 07)一样。

Nominally, the array elements should be unsigned char to avoid problems with values and wasted space, but any integral type could be used, more or less wastefully. 名义上,数组元素应为unsigned char以避免出现值和浪费空间的问题,但是可以使用任何整数类型,或多或少地浪费了。 You'd get interesting results if the type is signed char and the high bits are set on the values, or if the type is plain char and plain char is a signed type. 如果类型是带signed char并且在值上设置了高位,或者类型是纯char并且纯char是带符号的类型,您将得到有趣的结果。 You could also get interesting results from GETBIT if the type of A is an integer type bigger than char and the values in the array have bits set outside the last (least significant) 8 bits of the number. 如果A的类型是大于char的整数类型,并且数组中的值设置为该数字的最后8个(最低有效)位,则还可以从GETBIT获得有趣的结果。

What the macros do not do 宏不做什么

The macros provided by the professor are an object lesson in how not to write C preprocessor macros. 教授提供的宏是关于如何不编写C预处理器宏的对象课程。 They do not teach you how to write good C; 他们没有教你如何写好的C。 they teach how to write appallingly awful C. 他们教如何写可怕的C语言。

Each of those macros is dangerously broken because the argument k is not wrapped in parentheses when used. 这些宏中的每一个都被破坏,因为参数k在使用时没有括在括号中。 It isn't hard to argue that the same applies to A too. 不难争辩说同样适用于A The use of 01 and 07 isn't exactly wrong, but octal 01 and 07 are the same as decimal 1 and 7 . 使用0107并不是完全错误,但是八进制的0107与十进制的17相同。

The GETBIT macro needs an extra level of parentheses around its whole body, too. GETBIT宏也需要在其整个主体上附加一个括号。 Given 给定

int y = 2;
unsigned char array[32] = "abcdefghijklmnopqrstuvwxyz01234";

then this does not compile: 那么这不会编译:

int x = GETBIT(array + 3, y + 2) + 13;

This does compile (with warnings) if your compiler options are lax enough, but would produce an eccentric result: 如果您的编译器选项不够宽松,则会编译(带有警告),但是会产生异常结果:

int x = GETBIT(3 + array, y + 2) + 13;

and that's before we try discussing: 那是在我们尝试讨论之前:

int x = GETBIT(3 + array, y++) + 13;

The CLRBIT and SETBIT macros use braces which means that you can't write: CLRBIT和SETBIT宏使用花括号,这意味着您不能编写:

if (GETBIT(array, 13))
    SETBIT(array, 27);
else
    CLRBIT(array, 19);

because the semicolon after SETBIT is a null statement after the close brace in the statement block introduced by SETBIT , so the else clause is simply syntactically incorrect. 因为SETBIT后的分号是SETBIT引入的语句块中的SETBIT括号后的空语句,所以else子句在语法上完全不正确。

The macros could be written like this (retaining the statement block structure for the SETBIT and CLRBIT macros): 可以这样写宏(保留SETBITCLRBIT宏的语句块结构):

#define SETBIT(A, k) do { (A)[(k) >> 3] |= (1 << ((k) & 7)); } while (0)
#define CLRBIT(A, k) do { (A)[(k) >> 3] &= ~(1 << ((k) & 7)); } while (0)
#define GETBIT(A, k) (((A)[(k) >> 3] & (1 << ((k) & 7))) >> ((k) & 7))

The do { … } while (0) notation is a standard technique in macros that gets around the problem of breaking if / else statements. do { … } while (0)表示法是宏中的一种标准技术,可解决中断if / else语句的问题。

The macros could also be rewritten like this because assignments are expressions: 宏也可以这样重写,因为赋值是表达式:

#define SETBIT(A, k) ( (A)[(k) >> 3] |=  (1 << ((k) & 7)))
#define CLRBIT(A, k) ( (A)[(k) >> 3] &= ~(1 << ((k) & 7)))
#define GETBIT(A, k) (((A)[(k) >> 3] &   (1 << ((k) & 7))) >> ((k) & 7))

Or, even better, as static inline functions like this: 或者,甚至更好,因为像这样的static inline函数:

static inline void SETBIT(unsigned char *A, int k) { A[k >> 3] |=  (1 << (k & 7)); }
static inline void CLRBIT(unsigned char *A, int k) { A[k >> 3] &= ~(1 << (k & 7)); }
static inline int  GETBIT(unsigned char *A, int k) { return (A[k >> 3] & (1 << (k & 7))) >> (k & 7); }

The whole can be assembled into a simple test program: 整体可以组装成一个简单的测试程序:

#if MODE == 1

/* As provided */
#define SETBIT(A, k) { A[k >> 3] |= (01 << (k & 07)); }
#define CLRBIT(A, k) { A[k >> 3] &= ~(01 << (k & 07)); }
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)

#elif MODE == 2

/* As rewritten */
#define SETBIT(A, k) do { (A)[(k) >> 3] |= (1 << ((k) & 7)); } while (0)
#define CLRBIT(A, k) do { (A)[(k) >> 3] &= ~(1 << ((k) & 7)); } while (0)
#define GETBIT(A, k) (((A)[(k) >> 3] & (1 << ((k) & 7))) >> ((k) & 7))

#else

/* As rewritten */
static inline void SETBIT(unsigned char *A, int k) { A[k >> 3] |=  (1 << (k & 7)); }
static inline void CLRBIT(unsigned char *A, int k) { A[k >> 3] &= ~(1 << (k & 7)); }
static inline int  GETBIT(unsigned char *A, int k) { return (A[k >> 3] & (1 << (k & 7))) >> (k & 7); }

#endif

int main(void)
{
    int y = 2;
    unsigned char array[32] = "abcdefghijklmnopqrstuvwxyz01234";
    int x = GETBIT(array + 3, y + 2) + 13;
    int z = GETBIT(3 + array, y + 2) + 13;

    if (GETBIT(array, 3))
        SETBIT(array, 22);
    else
        CLRBIT(array, 27);

    return x + z;
}

When compiled with -DMODE=2 or -DMODE=0 or without any -DMODE setting, then it is clean. 当使用-DMODE=2-DMODE=0或没有任何-DMODE设置进行-DMODE ,它是干净的。 When compiled with -DMODE=1 , there are an objectionable number of warnings (errors for me because I use GCC and compile with -Werror which makes any warning into an error). 当使用-DMODE=1编译时,会有令人反感的警告数量(对我来说是错误的,因为我使用GCC并使用-Werror进行编译,这会使任何警告变为错误)。

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -DMODE=0 bits23.c -o bits23 
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -DMODE=2 bits23.c -o bits23
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -DMODE=1 bits23.c -o bits23
bits23.c: In function ‘main’:
bits23.c:28:33: error: suggest parentheses around ‘+’ inside ‘>>’ [-Werror=parentheses]
     int x = GETBIT(array + 3, y + 2) + 13;
                                 ^
bits23.c:6:25: note: in definition of macro ‘GETBIT’
 #define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
                         ^
bits23.c:6:24: error: subscripted value is neither array nor pointer nor vector
 #define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
                        ^
bits23.c:28:13: note: in expansion of macro ‘GETBIT’
     int x = GETBIT(array + 3, y + 2) + 13;
             ^
bits23.c:28:33: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses]
     int x = GETBIT(array + 3, y + 2) + 13;
                                 ^
bits23.c:6:43: note: in definition of macro ‘GETBIT’
 #define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
                                           ^
bits23.c:28:33: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses]
     int x = GETBIT(array + 3, y + 2) + 13;
                                 ^
bits23.c:6:57: note: in definition of macro ‘GETBIT’
 #define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
                                                         ^
bits23.c:29:33: error: suggest parentheses around ‘+’ inside ‘>>’ [-Werror=parentheses]
     int z = GETBIT(3 + array, y + 2) + 13;
                                 ^
bits23.c:6:25: note: in definition of macro ‘GETBIT’
 #define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
                         ^
bits23.c:29:33: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses]
     int z = GETBIT(3 + array, y + 2) + 13;
                                 ^
bits23.c:6:43: note: in definition of macro ‘GETBIT’
 #define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
                                           ^
bits23.c:29:22: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses]
     int z = GETBIT(3 + array, y + 2) + 13;
                      ^
bits23.c:6:23: note: in definition of macro ‘GETBIT’
 #define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
                       ^
bits23.c:29:33: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses]
     int z = GETBIT(3 + array, y + 2) + 13;
                                 ^
bits23.c:6:57: note: in definition of macro ‘GETBIT’
 #define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07)
                                                         ^
bits23.c:29:38: error: suggest parentheses around ‘+’ inside ‘>>’ [-Werror=parentheses]
     int z = GETBIT(3 + array, y + 2) + 13;
                                      ^
bits23.c:33:5: error: ‘else’ without a previous ‘if’
     else
     ^
cc1: all warnings being treated as errors
$

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM