0

I need help solving this problem in my mind, so if anyone had a similar problem it would help me.

Here's my code:

char c=0xAB;
printf("01:%x\n", c<<2);
printf("02:%x\n", c<<=2);
printf("03:%x\n", c<<=2);

Why the program prints:

01:fffffeac
02:ffffffac
03:ffffffb0

What I expected to print, that is, what I got on paper is:

01:fffffeac
02:fffffeac
03:fffffab0

I obviously realized I didn't know what the operator <<= was doing, I thought c = c << 2.

If anyone can clarify this, I would be grateful.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • I reviewed it, but it didn’t help me :( – LogicNotFound Nov 04 '21 at 12:14
  • How do we get for the second printf: ffffffac ? So how is the first print different from the second? – LogicNotFound Nov 04 '21 at 12:17
  • 1
    I believe it has to do with the first one being promoted to `int` and printed as an `int`, and the 2nd one promoted to `int` (for the shift) then back to `char` (to be stored in `c`) then back to `int` again to be printed. – 001 Nov 04 '21 at 12:21
  • Yes it is most likely, now I have noticed it too – LogicNotFound Nov 04 '21 at 12:25
  • Technically you have *undefined behavior* in your code: The format specifier `%x` is for **unsigned** integers, while the result of your operations will be a signed integer. Such mismatch between format specifier and argument type is UB. – Some programmer dude Nov 04 '21 at 12:37
  • @Someprogrammerdude That's debatable. See https://stackoverflow.com/questions/4664100/does-printfx-1-invoke-undefined-behavior – dbush Nov 04 '21 at 12:39
  • `char c; ... c<<=2` is very fishy code and likely broken. To begin with, drop `char` and use `uint8_t`. – Lundin Nov 04 '21 at 12:40

2 Answers2

3

You're correct in thinking that

c <<= 2

is equivalent to

c = c << 2

But you have to remember that c is a single byte (on almost all systems), it can only contain eight bits, while a value like 0xeac requires 12 bits.

When the value 0xeac is assigned back to c then the value will be truncated and the top bits will simply be ignored, leaving you with 0xac (which when promoted to an int becomes 0xffffffac).

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    The left shifts of negative values are not defined by the C standard, and the assignments of out-of-range values to signed `char` are implementation-defined. `char` is a byte in C by definition; it is eight bits on almost all systems. – Eric Postpischil Nov 04 '21 at 12:36
0

<<= means shift and assign. It's the compound assignment version of c = c << 2;.

There's several problems here:

  • char c=0xAB; is not guaranteed to give a positive result, since char could be an 8 bit signed type. See Is char signed or unsigned by default?. In which case 0xAB will get translated to a negative number in an implementation-defined way. Avoid this bug by always using uint8_t when dealing with raw binary bytes.

  • c<<2 is subject to Implicit type promotion rules - specifically c will get promoted to a signed int. If the previous issue occured where your char got a negative value, c now holds a negative int.

  • Left-shifting negative values in C invokes undefined behavior - it is always a bug. Shifting signed operands in general is almost never correct.

  • %x isn't a suitable format specifier to print the int you ended up with, nor is it suitable for char.

As for how to fix the code, it depends on what you wish to achieve. It's recommended to cast to uint32 before shifting.

Lundin
  • 195,001
  • 40
  • 254
  • 396