【问题标题】:Implementing bitwise operations in C在 C 中实现按位运算
【发布时间】:2021-09-13 03:29:07
【问题描述】:

我正在尝试在 C 中实现两个按位函数:

set(unsigned *x, unsigned n, unsigned v) --> 将值 x 的第 n 位设置为值 v。V 为 0 或 1,为简单起见,N 为 [0, 31]。

flip(unsigned *x, unsigned n) --> 翻转 x 中的第 N 位。为简单起见,假设 N 为 [0,31]。

我的实现:


void set(unsigned *x, unsigned n, unsigned v) {
 
    *x = (1 << n) | v;
    
}


void flip(unsigned *x, unsigned n) {
    
    *x = n ^ (1 << *x);

}

我的问题:这些功能不符合逻辑。

测试用例的输出:

测试 set_bit()

set_bit(0x0000004e,2,0) 返回 0x00000004 但我们预期为 0x0000004a set_bit(0x0000006d,0,0) 返回 0x00000001 但我们预期为 0x0000006c set_bit(0x0000004e,2,1) 返回 0x00000005 但我们预期为 0x0000004e set_bit(0x0000006d,0,1) 返回 0x00000001 但我们预期为 0x0000006d set_bit(0x0000004e,9,0) 返回 0x00000200 但我们预期为 0x0000004e set_bit(0x0000006d,4,0) 返回 0x00000010 但我们预期为 0x0000006d set_bit(0x0000004e,9,1) 返回 0x00000201 但我们预期为 0x0000024e set_bit(0x0000006d,7,1) 返回 0x00000081 但我们预期为 0x000000ed

测试flip_bit()

flip_bit(0x0000004e,0) 返回 0x00004000,但我们预期为 0x0000004f flip_bit(0x0000004e,1) 返回 0x00004001,但我们预期为 0x0000004c flip_bit(0x0000004e,2) 返回 0x00004002,但我们预期为 0x0000004a flip_bit(0x0000004e,5) 返回 0x00004005,但我们预期为 0x0000006e flip_bit(0x0000004e,9) 返回 0x00004009,但我们预期为 0x0000024e

【问题讨论】:

  • 对于set_bit,您不认为*x 之前的值应该出现在赋值右侧的某处吗?用纸和铅笔尝试一些例子。
  • 1 &lt;&lt; *x 不能在flip_bit 中正确; *x 不是位索引(不必在 0..31 范围内)。
  • 这篇文章的原始版本特别声明不允许循环和条件。现在显然已经消除了这种情况。不要那样做。如果你改变了你想要什么的想法,那是一个新的、不同的问题。尽管循环对此无济于事,但条件语句可以,并且最好的解决方案将使用它们(if/else 或条件 ?: 运算符)。当然,没有它们也可以完成,但这不是最好的方法。
  • 这能回答你的问题吗? How do you set, clear, and toggle a single bit?
  • 在我链接的问题中,请注意接受的答案中的“将第 n 位更改为 x”部分。

标签: c function binary bitwise-operators


【解决方案1】:

您应该为变量命名,而不是必须用 cmets 解释的 1 个字符的名称。

我们不应该在进行按位运算时使用有符号数字,并且1 是有符号整数。所以1&lt;&lt;31 是一个错误。

此外,您的函数集不能用于将位设置为值 0,因为按位 OR 不能那样工作。您要么需要单独的函数来设置/清除,要么需要在函数中包含一个分支if 语句,例如:

if(bit_value) 
  *value |= 1u<<n;
else
  *value &= ~(1u<<n);

可以与any 类型一起使用的更明智的接口是使用字符数组。然后你可以传递一个指向整数的指针,强制转换它,它保证可以工作。

void bit_set (unsigned char* data, size_t byte, unsigned char bit)
{
  data[byte] |= 1u << bit;
}

void bit_clear (unsigned char* data, size_t byte, unsigned char bit)
{
  data[byte] &= ~(1u << bit);
}

void bit_toggle (unsigned char* data, size_t byte, unsigned char bit)
{
  data[byte] ^= 1u << bit; 
}

【讨论】:

    【解决方案2】:

    如果我们在逻辑上遵循您的代码,我们可以看到正在发生的事情。让我们使用输入 0x4e 和输出 0x04 的第一个示例。为简单起见,我只看一个字节。

    首先,您将 1 移位 N 次。在这种情况下,N = 2。在二进制中,它看起来像这样:

    value    : N
    ---------:--
    00000001 : 0
    00000010 : 1
    00000100 : 2
    

    接下来,我们将 V 与 V = 0 的值按位或运算:

    00000100 | 00000000 => 00000100
    

    最后,该值被分配给地址 X 处的值。在二进制中,值 00000100 相当于十六进制的 0x04,与您的实际输出相匹配。

    既然我们已经确定了逻辑错误,我们就可以提出适当的指示来帮助您到达您想去的地方。我提出以下建议(尽管可能还有其他更好的方法)。

    void set_bit(unsigned *x, unsigned n, unsigned v) {
     
        unsigned temp = (1 << n);
        *x = v ? *x | temp : *x & ~temp;
        
    }
    

    这里发生的事情是首先我们存储由位移计算的值:00000100。然后我们根据 V 的值选择执行两条指令之一。

    if v == 1
        X        : TEMP                 : HEX
        01001110 | 00000100 => 01001110 : 0x4e
    if v == 0
        X        : ~TEMP                 : HEX
        01001110 & 11111011 => 01001010 : 0x4a
    

    当 V = 0 时,这应该可以让您获得所需的输出。

    另一个函数实际上要简单得多。我们可以做一个按位异或:

    void flip_bit(unsigned *x, unsigned n) {
        
        unsigned temp = (1 << n);
        *x ^= temp;
    
    }
    

    这就是当 N = 0 时它在二进制中的样子:

    X        : TEMP                 : HEX
    01001110 ^ 00000001 => 01001111 : 0x4f
    

    再次,这符合您的预期输出。

    如果其中有任何令人困惑的地方,可以通过简单的谷歌搜索来了解按位运算符的工作原理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多