Notice though that the original problem is a bit ambiguous. There might be another wrinkle. What if one of the bits in b is 0? Does that mean the function should not set the bit (as the examples in this thread have done), or does it mean it should /clear/ that bit of a?

Anyway, the latter is very easy to implement as well. Just clear those bits in a that are going to be overwritten with bits from b.

Code:

#include <limits.h> /* for CHAR_BIT */
unsigned int setbits(unsigned int a, int p, int n, unsigned int b)
{
const unsigned int bits = sizeof(unsigned int) * CHAR_BIT;
unsigned int mask = (unsigned int)-1; /* set mask to be all 1's */
mask <<= bits - n;
mask >>= bits - p;
/* now mask contains 1's where a's bits are to be replaced with b's bits */
b <<= bits - n;
b >>= bits - p;
a &= ~mask; /* clear bits of a that are to be replaced by bits from b */
return a | b;
}

Ah, there must be a better way. Let's see. You could assemble the resulting number piecewise.

Code:

#include <stdio.h>
#include <limits.h> /* for CHAR_BIT */
unsigned setbits(unsigned a, int p, int n, unsigned b)
{
const unsigned bits = sizeof(unsigned) * CHAR_BIT;
unsigned left_mask, right_mask;
unsigned value = 0;
left_mask = (unsigned)-1 << (p + n);
right_mask = (unsigned)-1 >> (bits - p);
value |= a & left_mask;
value |= (b << p) & ~left_mask;
value |= a & right_mask;
return value;
}
int main() {
printf("%x\n", setbits(0xf0, 2, 3, -1u));
return 0;
}

[Coloured with codeform.]

(Notice my leaving out of "int" in "unsigned int", because it's not necessary.)

Not sure if that's right, and I don't feel like testing it any further, but you get the idea even if it's wrong. I hope.

left_mask is 1's where the result should copy bits from a, left of the inserted bits from b. right_mask is 1's where the result should copy bits from a, right of bits from b.