首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C设置一个位(位操作)

C设置一个位(位操作)
EN

Stack Overflow用户
提问于 2016-09-05 15:46:14
回答 2查看 468关注 0票数 7

我正在对一个工业plc进行编程,我必须处理一些位,以便与VFD进行profi总线通信。我得到一个2字节的状态,并且必须发送2字节的命令。对于此操作,我必须设置位以使VFD运行。例如:

代码语言:javascript
运行
复制
                   Byte n+1           Byte n
PLC    -->  --------------------- ---------------   --> VFD
            15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
            ---------+--------- | | | | -+- | | +- 0: Reglersperre / Freigabe
                     |          | | | |  |  | +--- 1: Freigabe / Schnellstopp
                     |          | | | |  |  +----- 2: Freigabe / Halt
                     |          | | | |  +-------- 3..4: reserviert = 0
                     |          | | | +------------5: Parametersatz-Umschaltung
                     |          | | +------------- 6: Reset
                     |          | +--------------- 7: reserviert = 0
                     |          |
                     |          +----------------- 8: Lüften der Bremse ohne Antreibsfreigabe
                     +---------------------------- 9..15: reserviert = 0

因此,我必须将位0置1,才能将VFD设置为工作模式。然后我需要设置位2来启动驱动器。

现在我找到了一个描述比特处理的question,我认为这个解决方案应该可以工作,但我并不是真的理解它。

有没有人能解释一下为什么这个方法有效或者不有效?

代码语言:javascript
运行
复制
uint16_t change_bit(uint16_t command, unsigned int bit_nr, unsigned int val) {
  /* command = 2byte command; bit_nr = bit to manipulate;
     val = value bit should get (1;0) */
  command ^= (-val ^ command) & (1U  << bit_nr);
  return command;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-05 18:51:56

这确实是一个巧妙的技巧,可以在没有分支的情况下进行一些修改。这里有一个假设你理解按位运算符是如何工作的解释。

让我们从重新排列表达式开始

代码语言:javascript
运行
复制
(-val ^ command) & (1 << bit_nr)

首先,让我们交换-valcommand

代码语言:javascript
运行
复制
(command ^ -val) & (1 << bit_nr)

然后,应用分配律

代码语言:javascript
运行
复制
(command & (1 << bit_nr)) ^ (-val & (1 << bit_nr))

现在,认识到(假设二进制补码)如果val为0,-val为0(未设置位),如果val为1,-val为-1 (所有位均已设置)

代码语言:javascript
运行
复制
(command & (1 << bit_nr)) ^ (val ? (1 << bit_nr) : 0)

因此可以将赋值重写为if语句

代码语言:javascript
运行
复制
if (val)
    command ^= (command & (1 << bit_nr)) ^ (1 << bit_nr);
else
    command ^= command & (1 << bit_nr);

如果val为1,则bit_nr位置的位为XORed,其取反的值始终设置该位。如果val为0,则该位为XORed,其自身始终清除该位。所有其他位都为0的XORed,保持不变。

下面是一个可读性更好的无分支版本,它用位操作换来了移位操作:

代码语言:javascript
运行
复制
uint16_t change_bit(uint16_t command, unsigned int bit_nr, unsigned int val) {
  // Always clear the bit.
  command &= ~(1u << bit_nr);
  // Set the bit if val == 1.
  command |= val << bit_nr;
  return command;
}
票数 2
EN

Stack Overflow用户

发布于 2016-09-05 16:19:13

这似乎是有效的,但它是非常令人惊讶的,并不是很清楚。有人说它“太聪明了”。一种更清晰的方法可能是:

代码语言:javascript
运行
复制
uint16_t change_bit(uint16_t command, unsigned int bit, bool value)
{
  const uint16_t mask = 1u << bit;
  if(value)
    return command | mask;
  else
    return command & ~mask;
}

这里面有一个跳跃( if),但是一个聪明的编译器可能会优化它。如果它不是非常关键的性能代码,它通常是更好的清晰。

请注意,在进行位级操作时,使用无符号类型通常是一个好主意。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39325931

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档