C语言中的位运算

3
如果我有一个32位的整数。前28位(从左边开始)用于存储内存块的大小,接下来的两位是0,最后两位分别是:
- 用于存储是否为最后一个节点 - 用于存储是否被使用(分别)
我想知道如何在isLast操作和isUsed操作中打开和关闭标志。
(如果我们只考虑最后两个整数(再次从左边开始),那么01将不是最后一个并且被使用,另一个例子是11是最后一个并且被使用,00既不是最后一个也没有被使用。)
我希望能够以简单的方式打开和关闭标志。我知道我需要使用位运算符,包括&和|,但我不确定如何操作。
如果需要更多问题描述,请随时向我提问。
7个回答

12
//turn on isUsed
data |= 1;
//turn off isUsed
data &= ~1;
//turn on notLast
data &= ~2;
//turn off notLast
data |= 2;

也许可以加入一个"int const USED_FLAG 1" / "int const LAST_FLAG 2"吗? - Sebastian
我认为 "data |= 2" 的意思是它是最后一个节点,而不是相反的。 - Sebastian
3
你的最后一个标志似乎与原始问题相反。换句话说,data |= 2 打开了最后一个标志,而 data &= ~2 关闭了它。 - Left For Archive
1
XOR将切换位,而不是关闭它。使用OR进行设置,使用NOT AND取消设置,使用XOR进行切换。 - David Rodríguez - dribeas
@pmg 嗯,我几分钟前将它从&= ~改为了^=。我需要进行调查(还发现回滚似乎会清除历史记录,我希望能返回到以前的版本,哎!) - Andreas Brinck
显示剩余2条评论

12

这很简单:

/* Turn on bit 0 */
code = code | 1;

/* Turn off bit 0 */
code = code & ~1;

/* Turn on bit 1 */
code = code | 2;

/* Turn off bit 1 */
code = code & ~2;

请查看C语言位运算符,或者在Google搜索相关术语。任何有关C语言的书籍或教程中都可以找到此内容。


5

一般来说,从最不重要的位开始计数,要设置第N位,需要将原始值与1 << N进行按位或运算。

例如,要设置第1位:

val |= (1 << 1);

要清除第N位,需要将原始值与 1 << N 的按位取反进行 AND 运算。

例如,清除第1位:

val &= ~(1 << 1);

4
这需要一个接口,可以是函数或宏,类似于:
``` 这里放置代码 ```
// Use unsigned ints (assuming that's your 32-bit type).

#define setLast(x)   (x) |=  2
#define clrLast(x)   (x) &= ~2
#define isLast(x)    ((x) &  2)

#define setUsed(x)   (x) |=  1
#define clrused(x)   (x) &= ~1
#define isUsed(x)    ((x) &  1)

您也可以提供宏来提取大小部分并创建整数:
#define getSize(x) ((x) >> 4)
#define create (sz,last,used) \
    (((sz) & 0x0fffffff) << 4) | \
    (((last) & 1) << 1) | \
    (((used) & 1))

如果您提供“函数”并为它们起一个合理的名称,那么您的代码将变得更易读,就像上述内容一样。否则,您的代码将充斥着难以理解的位操作指令。
请记住宏的常规规则,例如如果您的宏使用多次,则不要传入像 x++ 这样的东西(实际上在这里并非如此)。如果您想要更加安全,可以将它们编写成函数。
等效的函数应该是:
unsigned int setLast (unsigned int *x) { *x |=  2; return *x; }
unsigned int clrLast (unsigned int *x) { *x &= ~2; return *x; }
unsigned int isLast  (unsigned int  x) { return x & 2; }

unsigned int setUsed (unsigned int *x) { *x |=  1; return *x; }
unsigned int clrUsed (unsigned int *x) { *x &= ~1; return *x; }
unsigned int isUsed  (unsigned int  x) { return x & 1; }

unsigned int getSize (insigned int  x) { return x >> 4; }
unsigned int create  (unsigned int sz, unsigned int last, unsigned int used) {
    unsigned int ret =
        ((sz & 0x0fffffff) << 4) |
        ((last & 1) << 1) |
        ((used & 1));
    return ret;
}

1
如果你将它们实际上变成函数,那么代码会更易读。此外,在每个宏中,x 应该是 (x),以避免意外的优先级问题。 - anon

3

打开标志:

register |= (1<<LAST_BIT);

关闭标志:
register &= ~(1<<LAST_BIT);

另一种方法是使用联合位域:

union
{
  uint32_t value;
  struct
  {
    unit32_t body:28;
    unit32_t reserved:2;
    unit32_t last_bit:1;
    unit32_t used_bit:1;
  } fields;
} MyResister;

MyResister.fields.last_bit = 1;
MyResister.fields.used_bit = 0;

我不建议这样做,因为无法保证bodyreserved等变量在内存中的紧密排列,也就是说:sizeof(MyRegister)可能等于sizeof(uint32_t),也可能不等。请参考标准9.6.1。 - Andreas Brinck
位域在不同编译器之间是非常不可移植的,且与编译器有关。然而,许多编译器(尤其是针对嵌入式处理器的编译器)表现得符合预期。在这种情况下,它们非常方便。 - kgiannakakis

0
我会加入一个BIT(x)宏,以使源代码更清晰:
#define BIT(n) (0x1U << (n))

这将导致:

#define LAST_SET(x) ((x) |= BIT(1))
#define LAST_CLR(x)  ((x) &= ~BIT(1))

另外,正如先前所述,始终将参数放在括号中。

(OT) 编辑:更改宏的名称,因为我不喜欢动词在前。首先,像 getWhatever 这样的函数适用于可以将函数分组到类中的代码。在 C 中,我认为应该先放置“组件”名称,例如 timeGet() 等

(OT2) 如果是这样的寄存器宏,则会产生更好的可移植性:

#define MY_REG_RD() (MY_REG)
#define MY_REG_WR(x) (MY_REG = (x))
#define MY_REG_SET(x) (MY_REG |= (x))
#define MY_REG_CLR(x) (MY_REG &= ~(x))
#define MY_REG_DIS BIT(10)
#define MY_REG_EN BIT(4)

然后你可以这样做:

MY_REG_SET(MY_REG_EN);

(OT) 我大部分同意你的观点。我的意思是以大端格式命名变量:最重要的单词先写。比较一下 timeGet()timeSet()timeAdvance() ... 其中 time 是最重要的单词 ... 与 fixTime()fixLength()fixWeight() ... 其中 "修复" 更为重要 :) - pmg
@Henrik,我认为你的LAST_CLR应该使用~BIT(1),是吗? - paxdiablo
大部分都很好,除了int变量现在硬编码为MY_REG,你希望每次都进行def/undef吗?现在这些宏影响的内存更加难以理解。 - catchmeifyoutry
抓住我如果你能,我并不是指这应该用于常规的内存变量。这是针对ASIC中的寄存器。我应该更清楚地表达,抱歉。 - Henrik

0
bool isBitOn( int mask , int i ){ // returns True if i-Th bit is On
    return mask & ( 1 << i ) ;
}

int BitOn( int mask , int i ){ // Turn On the i-Th bit of the value and then returns it
    return mask | ( 1 << i ) ;
}

int BitOff( int mask , int i ){ // Turn Off the i-Th bit of the value and then returns it
    return mask - ( 1 << i ) ;
}

int BitToggle( int mask , int i ){ // Toggle the i-Th bit of the value and then returns it
    return mask ^ ( 1 << i ) ;
}

void printbit(int n) { // print the Binary representation of a Integer Number
    for(int i = 31 ; i >=0 ; i-- )
        printf("%d", isBitOn(n,i) );
    printf("\n");
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接