【问题标题】:How to replace bits in a bitfield without affecting other bits using c如何使用c替换位域中的位而不影响其他位
【发布时间】:2011-05-08 05:17:18
【问题描述】:

我想在不影响其他位的情况下替换 32/64 位数据字段中的位/位(大于 1)。例如:

我有一个 64 位寄存器,其中第 5 位和第 6 位可以取值 0、1、2、3。

5:6    
0 0
0 1
1 0
1 1     

现在,当我读取寄存器时,我得到的值是 0x146(0001 0 10 0 0110)。现在我想将位位置 5 和 6 的值更改为 01。(现在它是 10,即十进制的 2 和我想将其替换为 1 e 01) 而不影响其他位并写回仅修改位 5 和 6 的寄存器。(因此更改后变为 126)

我试过这样做

reg_data=0x146
reg_data |= 1 << shift   (shift in this case is 5)

如果我在位 5 和 6 处执行此值,将变为 11(0x3) 而不是我想要的 01(0x1)。

  • 如何进行读取/修改/写入?
  • 如何使用 C 仅替换 32/64 位字段中的某些位/位而不影响字段的整个数据?

设置一点没问题,但不止一点,我觉得有点困难。

非常感谢任何建议。

【问题讨论】:

    标签: c bit-manipulation


    【解决方案1】:

    为什么不使用位掩码?有点像:

    new_value = 0, 1, 2 or 3  // (this is the value you will set in)
    bit_mask = (3<<5)         // (mask of the bits you want to set)
    reg_data = (reg_data & (~bit_mask)) | (new_value<<5)
    

    这会保留旧位和新位中的 OR。

    【讨论】:

      【解决方案2】:
      reg_data &= ~( (1 << shift1) | (1 << shift2) );
      reg_data |= ( (1 << shift1) | (1 << shift2) );
      

      第一行清除 (​​shift1, shift2) 处的两位,第二行设置它们。

      【讨论】:

      • +1。此外,如果您知道它们是相邻的,它会变得更简单:例如,在这种情况下,reg_data = reg_data &amp; (~(3 &lt;&lt; 5)) | (1 &lt;&lt; 5)
      【解决方案3】:

      这是一个通用过程,它作用于一个长数组,认为它是一个长位域并单独寻址每个位位置

      #define set_bit(arr,x) ((arr[(x)>>3]) |= (0x01 << ((x) & 0x07)))
      #define clear_bit(arr,x) (arr[(x)>>3] &= ~(0x01 << ((x) & 0x07)))
      #define get_bit(arr,x) (((arr[(x)>>3]) & (0x01 << ((x) & 0x07))) != 0)
      

      简单地使用索引使用索引的低 3 btis 来标识 char 数组的每个位置内的 8 个不同的位位置,以及由x 表示的位出现的数组位置的高余位地址。希望这会有所帮助。

      编辑1: 要设置一个位,您需要将目标字与另一个字相或,在该特定位位置为 1,在所有其他字中为 0。其他位置的所有 0 确保目标中现有的 1 在 OR 期间保持原样,特定位置的 1 确保目标在该位置获得 1。如果我们有掩码 = 0x02 = 00000010(1 字节),那么我们可以将它与任何字进行 OR 以设置该位 pos

      target = 1 0 1 1 0 1 0 0
      OR       + + + + + + + +
      mask     0 0 0 0 0 0 1 0
               ---------------
      answer   1 0 1 1 0 1 1 0
      

      要清除一个位,您需要将目标字与另一个字在该特定位位置为 0 和全部为 1。所有其他位位置中的所有 1 确保在 AND 期间目标保留其在那些位置中的 0 和 1,并且要清除的位位置中的 0 也会将该位位置设置为目标字中的 0。如果我们有相同的掩码 = 0x02,那么我们可以准备这个掩码以通过 ~mask 清除

      mask  = 0 0 0 0 0 0 1 0 
      ~mask = 1 1 1 1 1 1 0 1
      AND     . . . . . . . .
      target  1 0 1 1 0 1 1 0
              ---------------
      answer  1 0 1 1 0 1 0 0
      

      【讨论】:

        【解决方案4】:
        1. 对位域应用掩码以保留您不想更改的位。这也将清除您将要更改的位。

        2. 确保您的位域仅包含您要设置/清除的位。

        3. 要么使用 or 运算符来“或”两个位域,要么只是简单地相加。

        例如,如果您只想根据 0 到 15 的输入更改位 2 到 5。

        byte newVal = (byte)value & 0x0F;
        newVal = (byte)value << 2;
        oldVal = oldVal & 0xC3;
        oldVal = oldval + newVal;
        

        【讨论】:

          【解决方案5】:

          问题是关于如何在 C 中实现,但是当搜索“替换位”时,我将在 VB.Net 中提供我的实现。它已经过单元测试测试。对于那些想知道ToBinaryString 扩展是什么样子的人:Convert.ToString(value,2)

          ''' <summary>
          ''' Replace the bits in the enumValue with the bits in the bits parameter, starting from the position that corresponds to 2 to the power of the position parameter.
          ''' </summary>
          ''' <param name="enumValue">The integer value to place the bits in.</param>
          ''' <param name="bits">The bits to place. It must be smaller or equal to 2 to the power of the position parameter.</param>
          '''<param name="length">The number of bits that the bits should replace.</param>
          ''' <param name="position">The exponent of 2 where the bits must be placed.</param>
          ''' <returns></returns>
          ''' <remarks></remarks>'
          <Extension>
          Public Function PlaceBits(enumValue As Integer, bits As Integer, length As Integer, position As Integer) As Integer
              If position > 31 Then
                  Throw New ArgumentOutOfRangeException(String.Format("The position {0} is out of range for a 32 bit integer.",
                                                                      position))
              End If
              Dim positionToPlace = 2 << position
              If bits > positionToPlace Then
                  Throw New ArgumentOutOfRangeException(String.Format("The bits {0} must be smaler than or equal to {1}.",
                                                                      bits, positionToPlace))
              End If
          
              'Create  a bitmask (a series of ones for the bits to retain and a series of zeroes for bits to discard).'
              Dim mask As Integer = (1 << length) - 1
              'Use for debugging.'
              'Dim maskAsBinaryString = mask.ToBinaryString'
          
              'Shift the mask to left to the desired position'
              Dim leftShift = position - length + 1
              mask <<= leftShift
              'Use for debugging.'
              'Dim shiftedMaskAsBinaryString = mask.ToBinaryString'
          
              'Shift the bits to left to the desired position.'
              Dim shiftedBits = bits << leftShift
              'Use for debugging.'
              'Dim shiftedBitsAsBinaryString = shiftedBits.ToBinaryString'
          
              'First clear (And Not) the bits to replace, then set (Or) them.'
              Dim result = (enumValue And Not mask) Or shiftedBits
              'Use for debugging.'
              'Dim resultAsBinaryString = result.ToBinaryString'
          
              Return result
          End Function
          

          【讨论】:

            【解决方案6】:

            您需要一次做一点。使用 or 像您当前正在做的那样将位设置为 1,然后使用以下命令将某些内容设置为 0:

            reg_data &= ~ (1 << shift)
            

            【讨论】:

              【解决方案7】:

              您可以将此动态逻辑用于任意数量的位和任何位域。 基本上,您在一个数字位序列中有 3 个部分 -

              MSB_SIDE |更改部分 | LSB_SIDE

              CHANGED_PART 可以移动到最高 MSB 或 LSB 端。

              替换多个位的步骤如下 - 1. 只取 MSB_SIDE 部分,并将所有剩余位替换为 0。 2. 通过在特定位置添加所需的位序列来更新新的位序列。 3. 用原始位序列的 LSB_SIDE 更新整个位序列。

              org_no = 0x53513C;
              upd_no = 0x333;
              start_pos = 0x6, bit_len = 0xA;
              temp_no = 0x0;
              
              temp_no = org_no & (0xffffffff << (bit_len+start_pos));  //this is step 1
              temp_no |= upd_no << start_pos;  //this is step 2
              org_no = temp_no | (org_no & ~(0xffffffff << start_pos));  //this is step 3`
              

              注意:使用 0xffffffff 的掩码被视为 32 位。您可以根据自己的要求进行相应更改。

              【讨论】:

                猜你喜欢
                • 2011-05-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-03-29
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多