手动更改 unsigned int 中的一组字节

Manually changing a group of bytes in an unsigned int

我正在使用 C 语言,我正在尝试弄清楚如何更改 32 位无符号整数中的一组位。

例如,如果我有

int a = 17212403u;

在二进制中,变成 1000001101010001111110011。现在,假设我标记了这些位,它们以小端格式排列,最右边的位代表一个,右边第二个代表两个,依此类推,我如何手动更改一组位?

例如,假设我想更改位,使第 11 位到第 15 位的十进制值为 17,这怎么可能?

我正在考虑通过这样做来获得该范围:

unsigned int range = (a << (sizeof(a) * 8) - 14) >> (28)

但我不确定从现在开始该往哪里走。

第 11 位到第 15 位是 5 位,假设您的意思是包括第 15 位。 5 位是十六进制值:0x1f

然后你把这5位左移11位:0x1f << 11

现在我们有了要在原始变量中清除的位 11 到 15 的掩码,我们通过按位反转掩码和具有反转掩码的变量来做到这一点:a & ~(0x1f << 11)

下一步是将值 17 移到第 11 位:17 << 11

然后我们按位或将其放入我们清除的5位中:

unsigned int b = (a & ~(0x1f << 11)) | (17 << 11)

您将 (1) 首先必须清除位 11..15,然后 (2) 然后根据您要设置的值设置这些位。要实现 (1),请创建一个 "mask",将所有位设置为 1,但要清除的位除外;然后使用 a & bitMask 将这些位设置为 0。然后,使用 | myValue 将位设置为所需的值。 使用移位运算符 << 将掩码和值放在正确的位置:

int main(int argc, char** argv) {

    // Let's assume a range of 5 bits
    unsigned int bitRange = 0x1Fu;  // is ...00000000011111

    // Let's assume to position the range from bit 11 onwards (i.e. move 10 left):
    bitRange = bitRange << 10;             // something like 000000111110000000000
    unsigned int bitMask = ~bitRange;      // something like 111111000001111111111
    unsigned int valueToSet = (17u << 10); // corresponds to 000000101110000000000

    unsigned int a = (17212403u & bitMask) | valueToSet;

    return 0;
}

这是解释发生了什么的长版本。简而言之,您还可以这样写:

unsigned int a = (17212403u & ~(0x1Fu << 10)) | (17u << 10)

考虑使用位域。这允许您命名和访问整数的子部分,就好像它们是结构的整数成员一样。

有关 C 位域的信息,请参阅: https://www.tutorialspoint.com/cprogramming/c_bit_fields.htm

下面是使用位域执行您想要的操作的代码。该结构的 "middle5" 成员包含位 11-15。 "lower11" 成员是低 11 位的填充符,因此 "middle5" 成员将在正确的位置。

#include <stdio.h>

void showBits(unsigned int w)
{
  unsigned int bit = 1<<31;
  while (bit > 0) 
  {
    printf("%d", ((bit & w) != 0)? 1 : 0);
    bit >>= 1;
  }
  printf("\n");
}

int main(int argc, char* argv[])
{
  struct aBitfield {
      unsigned int lower11: 11;
      unsigned int middle5:  5;
      unsigned int upper16: 16;
  };

  union uintBits {
    unsigned int     whole;
    struct aBitfield parts;
  };

  union uintBits b;

  b.whole = 17212403u;

  printf("Before:\n");
  showBits(b.whole);

  b.parts.middle5 = 17;

  printf("After:\n"); 
  showBits(b.whole);    
}

程序输出:

Before:
00000001000001101010001111110011
After:
00000001000001101000101111110011

当然,您会希望为各个字段使用更有意义的命名。

不过请注意,位域在不同平台上的实现方式可能不同 - 因此它可能无法完全移植。