如何使用c替换位域中的位而不影响其他位

时间:2011-05-08 05:17:18

标签: c bit-manipulation

我想在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位和第5位执行此值6将成为我想要的11(0x3)而不是01(0x1)。

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

设置一点是可以的,但不止一点,我发现它有点困难。

非常感谢任何建议。

7 个答案:

答案 0 :(得分:24)

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

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的新位。

答案 1 :(得分:10)

reg_data &= ~( (1 << shift1) | (1 << shift2) );
reg_data |= ( (1 << shift1) | (1 << shift2) );

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

答案 2 :(得分:7)

这是一个通用的进程,它作用于一个长数组,考虑到它是一个长位域并单独寻址每个位的位置

#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表示的位发生。希望这会有所帮助。

<强> EDIT1: 要设置一个位,您需要将目标字与该特定位位置为1的另一个字或与目标的所有其他字相对应。其他位置的所有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的另一个字进行AND运算并且总共为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

答案 3 :(得分:3)

问题是关于如何在C中实现,但是当搜索&#34;替换比特&#34;在这里,我将在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

答案 4 :(得分:2)

  1. 对位域应用掩码以保持您不想更改的位。这也将清除你将要改变的位。

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

  3. 使用或运算符“或”两个位域,或者只是简单地添加它们。

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

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

答案 5 :(得分:0)

你需要一次做一点。使用您当前正在进行的操作或将其设置为1,并使用以下内容将某些内容设置为0:

reg_data &= ~ (1 << shift)

答案 6 :(得分:0)

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

MSB_SIDE | CHANGED_PART | 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位。您可以根据您的要求进行相应更改。