1

Hey I am kinda new to C and I wanted to ask why this prints out 4 instead of 260?

#include <stdio.h>

int main()
{
    unsigned char x = 130;
    x *= 2;
    printf("%d\n", x);
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Alpha
  • 319
  • 2
  • 12
  • 3
    `x*=2` means `x = x*2` – Aziz Jan 30 '22 at 14:25
  • https://www.tutorialspoint.com/cprogramming/c_assignment_operators.htm – Stephen C Jan 30 '22 at 14:26
  • 4
    260 > greater than 255, max size of a unsigned char and oveflowed – OldProgrammer Jan 30 '22 at 14:26
  • 3
    and it prints 4 because it overflows. The type `unsigned char` cannot hold numbers above 255. – Aziz Jan 30 '22 at 14:26
  • More generally, it depends on the value of `CHAR_BITS` (defined in `limits.h`) on your platform. The maximum value that an `unsigned char` variable can hold is "2 ^ CHAR_BITS - 1" (where ^ indicates exponentiation). –  Jan 30 '22 at 14:30
  • @Aziz: This is not overflow. *Overflow* is a specific term in the C standard for when the specified result of an operation is not representable in the result type. With `x *= 2` and assuming common widths, the *usual arithmetic conversions* convert both operands, `x` and `2`, to `int`. Then 130•2 produces 260, which does not overflow `int`. Then this must be converted to `unsigned char`. This conversion is specified to wrap modulo 256, so it does not overflow. The result is defined to be 4. It it overflowed, the behavior would not be defined by the C standard. – Eric Postpischil Jan 30 '22 at 15:04
  • @EricPostpischil I meant the general term, "integer overflow", which is handled in C as you described. Thank you for the clarification :) – Aziz Jan 30 '22 at 15:13
  • Basically the term overflow in the context of C has a very specific meaning to signed numbers. Unsigned numbers "wrap-around" instead of overflow. Though as it happens in this specific case, there is neither overflow nor wrap-around, just a narrowing conversion to an unsigned type. – Lundin Jan 31 '22 at 10:22

5 Answers5

3

The *= operator is called multiplication assignment operator and is shorthand for multiplying the operand to the left with the operand to the right and assigning the result to the operand to the left. In this case, it's the same as:

x = x * 2;

Here integer promotion first takes place and the result of x * 2 is indeed 260.

However, an unsigned char can usually only carry values between 0 and 255 (inclusive) so the result overflows (wraps around) when you try assigning values above 255 and 260 % 256 == 4.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    You answered the title, but that's actually not the OP question. He seems to understand what `*=` should do, but doesn't understand why it prints 4. – sagi Jan 30 '22 at 14:29
  • @sagi Oups, ok, updated – Ted Lyngmo Jan 30 '22 at 14:29
  • There is no overflow; that term should be avoided for wrapping when wrapping is in fact the defined behavior and not an out-of-bounds exception. Also, the arithmetic is performed in the `int` type. It is the conversion back to `unsigned char` that must deal with the wrapping. – Eric Postpischil Jan 30 '22 at 15:06
  • @EricPostpischil In most texts I've come across it's been called overflow - with defined behavior. Had it been a signed type, I would have mentioned that it would have undefined behavior. I added a note about the integer promotion. Good point. – Ted Lyngmo Jan 30 '22 at 15:15
3

x *= 2 uses the multiplication assignment operator, a short hand notation for x = x * 2 where x is only evaluated once.

The behavior of this small program is non trivial:

  • The multiplication is performed after integer promotion of the arguments, which means the value of x, 130 is promoted to type int and the multiplication gives 260 as an int.
  • This value is converted to the destination type, unsigned char by repeatedly subtracting UCHAR_MAX+1, which is 256 on your system, until reaching a value in the range of the destination type. Hence x becomes 260 % 256 = 4.
  • Because it has an integer type smaller than int, x is promoted to int when passed to printf, so the format %d, which expects an int value, has defined behavior and produces 4.

Note that for exotic architectures (eg: digital signal processing chips) where unsigned char has more than 8 bits, some of the above discussion is irrelevant and the printf may print 260 or even have undefined behavior (if sizeof(int) == 1).

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Re “repeatedly subtracting `UCHAR_MAX`, which is probably 256 on your system”: The value subtracted is `UCHAR_MAX`+1, not `UCHAR_MAX`, and that is definitely 256, not just probably, because it must be at least 256 by requirement of the standard and no greater value would reduce 260 to 4. – Eric Postpischil Jan 30 '22 at 15:07
  • @EricPostpischil: good point, answer amended – chqrlie Jan 30 '22 at 16:36
1

According to language specification unsigned char data type takes 1 byte of memory so it can store values 0-255 and if you a value greater than 255 it will overflow beginning from zero. So 260(130 * 2) - 256 = 4 is assigned to your variable. https://learn.microsoft.com/en-us/cpp/cpp/cpp-type-system-modern-cpp?view=msvc-170#fundamental-built-in-types

  • 1
    Note: The size of a byte is not specified to be 8 bits so even though 8 bits is the most common, it's not a must. `CHAR_BIT` can be used to check how many bits there are in `char` types. – Ted Lyngmo Jan 30 '22 at 14:46
  • 1
    This is wrapping, not overflow. Wrapping is a defined behavior. Overflow is not. – Eric Postpischil Jan 30 '22 at 15:09
1

In the statement with the compound assignment operator

x*=2;

that is equivalent to

x = x * 2;

the operand x of the expression is converted to the type int due to the integer promotions and the result pf the expression is indeed equal to 260. But the result is assigned to the variable x that has the type unsigned char.

unsigned char x=130;

The value 260 can not be stored in such an object. As 260 is internally as an integer is represented like

0x00000104

then only the last byte having the value 0x4 is stored in the object and this value is outputted.

You could get the expected result if for example the type of the variable x would be changed at least like

unsigned short x=130;
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

The unsigned char can contain only from 0 to 256. When the value is over 256, the value is forced from 0 to 256.

javaboy
  • 85
  • 7