Shift and shift assignment giving different results

Viewed 47

My goal here is to remove byte-count+1 bits from the left of this 8-bit integer (or as you'd often call it, unsigned char.

Should be simple really, but this bit of code

uint8_t val = 0xC3;
uint8_t byte_count = 2;
uint8_t cc = val << (byte_count+1) >> (byte_count+1);
printf("%X", cc);

Gives me C3 as a result.

While this one

uint8_t val = 0xC3;
uint8_t byte_count = 2;
uint8_t cc = val;
cc <<= (byte_count+1);
cc >>= (byte_count+1);
printf("%X", cc);

Gives me just the 3.

Yes, I've tried putting a bunch of parenthesis around it.
Yes, I've tried casting everything in the expression to uint8_t.

Why does this happen?

1 Answers

Promotion.

Upon stumbling on a few keywords, I found promotion and googled it. Found this answer.

Smaller data types will be implicitly promoted to int/unsigned in an expression. This other answer explains it in detail.

Because our initial uint8_t operand is promoted to a higher bit count integer, then shifting it by (in this case) 3 and back will no longer "remove" those bits. This is because they'll no longer be outside the bit boundary in the first shift.

The same of course doesn't happen for the shift assign, you can't convert the left operand as it's an lvalue (meaning it represents memory, which isn't resizable). It also returns an lvalue representing the left operand, so that can't be promoted either.

printf("%d\n", sizeof(
    (uint8_t)2 + (uint8_t)3
));

//Output: 4
Related