位操作 - 如何使用 C 替换位域中的位而不影响其他位



我想替换 32/64 位数据字段中的位/位(多个),而不会影响其他位。例如:

我有一个 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   // In this case, 'shift' is 5

如果我这样做,位位置 5 和 6 的值将变为 11 (0x3),而不是我想要的 01 (0x1)。

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

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

使用位掩码。这有点像:

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。

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

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

这是一个通用过程,它作用于长数组,将其视为长位域,并单独处理每个位位置:

#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)

它只是获取索引,使用索引的下三位来标识 char 数组每个位置内的八个不同位位置,以及上部余数位地址,数组位置由 x 表示的位出现在其中。

要设置位,您需要将目标字与另一个字 OR 或,其中 1 位于该特定位位置,0 在所有其他与目标位置。其他位置的所有 0 确保目标中的现有 1 与 OR 期间相同,特定位置的 1 确保目标在该位置获得 1。如果我们有 mask = 0x02 = 00000010(1 字节),那么我们可以 OR 这个到任何单词来设置该位位置:

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
  1. 对位域应用掩码以保留不想更改的位。这也将清除您将要更改的部分。

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

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

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

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

问题是关于如何在 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

你需要一次做一点。使用 or,就像您当前所做的那样,将位设置为 1,然后使用以下方法将某些内容设置为 0:

reg_data &= ~ (1 << shift)

您可以将此动态逻辑用于任意位数和任意位字段。

基本上,您在数字位序列中有三个部分 -

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

相关内容

最新更新