C:位字段和按位运算符

时间:2015-11-15 03:29:01

标签: c bit-manipulation bit

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

# 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和索引(0-8)并在该索引处设置/获取/清除一点。我得到了这个,我得到了如何使用它。

我想知道的是每个宏的确如何。有人可以向我解释这个,我五岁吗?

1 个答案:

答案 0 :(得分:2)

宏做什么

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

但是,它不是使用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)将值1左移0..7位,具体取决于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中存在增量或类似内容,则会非常重要。

全部放在一起:

  • SETBIT在由k表示的8位值数组中设置A th 位(表示将其设置为1)。
  • CLRBIT在由k表示的8位值数组中重置A th 位(意思是将其设置为0)。
  • GETBIT在由k表示的8位值数组中找到A th 位中的值,并将其作为{{}返回1}}或0 - 这就是最终1的作用。

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

宏不做什么

教授提供的宏是如何不编写C预处理器宏的对象课程。他们不教你如何写好C;他们教如何写出令人震惊的可怕的C。

这些宏中的每一个都被危险地破坏了,因为参数char在使用时没有包含在括号中。不难说这同样适用于k。使用A01并非完全错误,但八进制0701与十进制071相同。

7宏在整个身体周围需要额外的括号。给定

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;

CLRBIT和SETBIT宏使用大括号,这意味着你不能写:

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

因为if (GETBIT(array, 13)) SETBIT(array, 27); else CLRBIT(array, 19); 之后的分号是SETBIT引入的语句块中的闭括号之后的空语句,所以SETBIT子句在语法上是不正确的。

宏可以像这样编写(保留elseSETBIT宏的语句块结构):

CLRBIT

#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进行编译时,会出现令人反感的警告数(对我来说是错误的,因为我使用GCC并使用-DMODE=1进行编译,这会对错误发出警告)。

-Werror