When you find it "unclear where to start counting" you have encountered one of the greatest problems in software development: extracting useful specifications from your stakeholders! In these situations, when we can't just ask, then we need to choose an interpretation that seems most useful and reasonable, and (this is the important bit), be very clear that we had to make a choice, and what we chose.
In this case, I would probably make a somewhat more specific name, and also add an explanatory comment:
/*
* Returns x with n bits inverted starting at bit p
* (counting from the left)
* E.g. invert_bits(0x0000, 4, 2) == 0x0c00 for 16-bit unsigned
*/
unsigned invert_bits(unsigned x, unsigned p, unsigned n)
That example in the comment would normally become one of the tests I use to verify the function.
I find it odd that we're working with unsigned
yet we use sizeof (int)
to calculate the width. Although we know that int
and unsigned int
have the same size, it's clearer to be consistent. Actually, I would just use sizeof x
, which then makes it easier to re-use the code - e.g. if we write a version for unsigned long
.
I think the arithmetic could be simplified if we consider that a mask with the leftmost p
bits unset is ~0u >> p
. Using invert_bits(0x0000, 4, 2)
again, as in the comment, we can visualise what we're doing:
0000111111111111 mask_p = ~0u >> p;
0000001111111111 mask_n = mask_p >> n;
0000110000000000 mask_p ^ mask_n
As a completed function, we can reuse a single mask
variable for this:
unsigned invert_bits(unsigned x, unsigned p, unsigned n)
{
unsigned mask = ~0u >> p;
mask ^= mask >> n;
return x ^ mask;
}
The nice thing about this is that it adapts automatically to the type's size, without needing any sizeof
at all in the computation.
In case I've provided too much spoiler here, I provide some follow-up exercises:
- Write the same function, but with the assumption that you count bits from the rightmost (least significant) bit.
- Write a version for
unsigned long
.
- Write versions that unconditionally set and reset the specified group of bits.
- Write a
main()
that tests whether the above functions work as advertise. Remember to return EXIT_SUCCESS
or EXIT_FAILURE
as appropriate.
Which code can be shared in the answers to these questions?