Search code examples
cpointersbit-manipulationbit

How do I set a new bit pattern in a certain position without changing the rest of the bits in C?


Suppose I have the following unsigned long val = 0xfedcba9876543210 and I want to change the 16 least significant bits to 0xabcd. So, the original value will be changed to unsigned long val = 0xfedcba987654abcd. I already have a function get that can return 0x3210, but I'm unsure how I can change this section of the value to 0xabcd. For more context, here is what I am trying to implement:

void set_pattern(unsigned long* val, int i, unsigned short new_pattern) {
  // my attempt
  unsigned short old_pattern = get(val, i); // ex: returns 0x3210 when i = 0
  unsigned short* ptr = NULL;
  ptr = &old_pattern;
  *ptr = new_pattern;
}

When I tried my attempt, it seemed to not set the new pattern as I expected. Any help or feedback is appreciated in helping me gain a better understanding of C.


Solution

  • To explain Nate's comment, you want to apply a bitmask to zero out the relevant bits, then apply the new bits with a bitwise or.

    Let's do it with 32 bits and you want to change the the least 8.

    Apply a bitmask to turn the least 8 bits to 0. val = val & ~0xff. ~0xff is 0xffffff00. Since 0 & x = 0, all the filled in bits will retain their value, and all the 0's will become 0 no matter their original value.

        0x12345678  val
    AND 0xffffff00  ~0xff
      = 0x12345600
    

    Now that the relevant bits have been masked out, turned to 0, we can overwrite just them with a bitwise or. val = val | new_value. x | 0 = x. The irrelevant bits of new_value are 0. x | 0 = x. They will retain val's value. The relevant bits of val are 0. 0 | x = x. They will retain new_value's value.

       0x12345600  val
    OR 0x000000ef  new_value
     = 0x123456ef
    

    If you want to replace different bits, you need to shift the bitmask and replacement value the appropriate amount.

    Let's say we want to replace 56 with ef instead. Each hex character is 4 bits, so we need to left shift both the bitmask and replacement value by 8 bits.

        0x12345678  val
    AND 0xffff00ff  ~(0xff << 8) == ~0xff00
      = 0x12340078
    
        0x12340078  val
    OR  0x0000ef00  new_value << 8 == 0xef00
     =  0x1234ef78