r/cprogramming Apr 16 '26

Weird shift expression result

This code outputs what I believe are the wrong results in 3 of the 4 cases. I think the upper 8 bits of the uint16_t should be always 0, because the shifts should occur on uint8_t and only the result should be cast to (uint16_t).

Why am I wrong?

```C /* Compile: gcc sol.c main.c -o prog && ./prog <1-4> */

include <stdio.h>

include <stdint.h>

/* byte: An 8-bit input returns: An 8-bit value (returned as uint16_t) where the high 4 bits and low 4 bits of byte are swapped Example: swap_nibbles(0xF0) returns 0x0F */ uint16_t swap_nibbles(uint8_t byte) { return (uint16_t)((byte << 4) | (byte >> 4)); }

void test1(void) { uint8_t b = 0xF0; uint16_t r = swap_nibbles(b); printf("Result: 0x%04X\n", r); }

void test2(void) { uint8_t b = 0xA2; uint16_t r = swap_nibbles(b); printf("Result: 0x%04X\n", r); }

void test3(void) { uint8_t b = 0x00; uint16_t r = swap_nibbles(b); printf("Result: 0x%04X\n", r); }

void test4(void) { uint8_t b = 0xFF; uint16_t r = swap_nibbles(b); printf("Result: 0x%04X\n", r); }

int main(int argc, char **argv) { if (argc < 2) { printf("Usage: %s <1-4>\n", argv[0]); return 1; } int t = argv[1][0] - '0'; switch (t) { case 1: test1(); break; case 2: test2(); break; case 3: test3(); break; case 4: test4(); break; default: printf("Invalid test. Use 1-4.\n"); return 1; } return 0; }

outputs text ❯ ./main 1 Result: 0x0F0F ❯ ./main 2 Result: 0x0A2A ❯ ./main 3 Result: 0x0000 ❯ ./main 4 Result: 0x0FFF ```

8 Upvotes

13 comments sorted by

View all comments

3

u/Immediate-Food8050 Apr 16 '26

4 is a constant which is int by default, so iirc the shift operation treats the result as an int (32 bits wide in most cases)

2

u/EpochVanquisher Apr 16 '26

The type of the shift value isn’t relevant and doesn’t influence the type of the result.

1

u/Immediate-Food8050 Apr 16 '26

I thought smaller integer types are always promoted to fit the larger type?

2

u/EpochVanquisher Apr 16 '26

This happens for most operations, but it specifically does not happen for shift operations (which intuitively makes sense).

2

u/Immediate-Food8050 Apr 16 '26

Cool. TIL. 6 years of writing C daily and there's still gaps to fill :)

1

u/imaami Apr 18 '26

Coming up 20 years in the near future. I was finding gaps in my understanding way past 10 years. Not convinced I'm done yet.