繁体   English   中英

C:位字段和按位运算符

[英]C: Bit fields and bitwise operators

我的教授为我们分配了一些使用位字段的作业,并给了我们三个宏

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

我们不需要/也不希望完全了解这些内容,而只是用来完成任务。 这些宏中的每个宏都采用unsigned int和and索引(0-8),并在该索引处设置/获取/清除位。 我得到了,我得到了如何使用它。

我想知道的是这些宏的每个功能。 像我五岁时,有人可以向我解释吗?

宏做什么

忽略下一节中概述的问题,宏将整数类型的数组视为8位值的数组,并且当要求对位k处理时,处理 k/8 元素的 k%8 位的数组。

但是,它没有使用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将值右移3位,有效除以8。
  • k & 07提取3个最低有效位(因为07八进制或7十进制为二进制111 ),忽略其余部分。
  • 01 << (k & 07)移动通过视的值0..7位向左值1 k & 07 ,产生的二进制值中的一个:

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

    形式上,它实际上是一个int值,因此可能具有32位,但是高阶位全为零。

  • ~运算符将每个0位转换为1,并将每个1位转换为0。

  • &运算符组合两个值,产生一个1位(两个位均为1)和0(其中一个或两个位均为0)。
  • | 运算符组合两个值,产生两个位均为0的0位和一个或两个位均为1的1。
  • 赋值运算符|=&=将RHS上的操作数应用于LHS上的变量。 符号a |= b; 等价于a = a | b; a = a | b; 除了a仅被评估一次。 这个细节在这里无关紧要; 如果表达式a存在增量或类似的内容,这将a

放在一起:

  • SETBIT设置由A表示的8位值数组中的 k位(意味着将其设置为1)。
  • CLRBIT重置由A表示的8位值数组中的 k位(意味着将其设置为0)。
  • GETBIT发现在值k 由下式表示的8位值的阵列中位A ,并将其返回作为要么01 -这就是最终>> (k & 07)一样。

名义上,数组元素应为unsigned char以避免出现值和浪费空间的问题,但是可以使用任何整数类型,或多或少地浪费了。 如果类型是带signed char并且在值上设置了高位,或者类型是纯char并且纯char是带符号的类型,您将得到有趣的结果。 如果A的类型是大于char的整数类型,并且数组中的值设置为该数字的最后8个(最低有效)位,则还可以从GETBIT获得有趣的结果。

宏不做什么

教授提供的宏是关于如何不编写C预处理器宏的对象课程。 他们没有教你如何写好的C。 他们教如何写可怕的C语言。

这些宏中的每一个都被破坏,因为参数k在使用时没有括在括号中。 不难争辩说同样适用于A 使用0107并不是完全错误,但是八进制的0107与十进制的17相同。

GETBIT宏也需要在其整个主体上附加一个括号。 给定

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

那么这不会编译:

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

如果您的编译器选项不够宽松,则会编译(带有警告),但是会产生异常结果:

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

那是在我们尝试讨论之前:

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

CLRBIT和SETBIT宏使用花括号,这意味着您不能编写:

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

因为SETBIT后的分号是SETBIT引入的语句块中的SETBIT括号后的空语句,所以else子句在语法上完全不正确。

可以这样写宏(保留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))

do { … } while (0)表示法是宏中的一种标准技术,可解决中断if / else语句的问题。

宏也可以这样重写,因为赋值是表达式:

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

或者,甚至更好,因为像这样的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); }

整体可以组装成一个简单的测试程序:

#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;
}

当使用-DMODE=2-DMODE=0或没有任何-DMODE设置进行-DMODE ,它是干净的。 当使用-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