0

I am trying to add '1' to a character which is holding maximum positive value it can hold. It is giving 0 as output instead of giving -256.

#include <stdio.h>

int main() {
    signed char c = 255;
    printf("%d\n", c + 1 );
}

O / P : 0
c + 2 = 1;
c + 3 = 2;

As per my understanding, it should give negative numbers once it reaches the maximum limit (). Is this correct? I am testing on Ubuntu.

kadina
  • 5,042
  • 4
  • 42
  • 83

2 Answers2

1

A signed char is very often 8-bit encoding values [-128...127].

signed char c = 255; is attempting to initialize c to a value outside the signed char range.

It is implementation behavior what happens next. Very commonly 255 is converted "mod" 256 to the value of -1.

signed char c = 255;
printf("%d\n", c     );  // -1 expected
printf("%d\n", c + 1 );  //  0 expected

As per my understanding, it should give negative numbers once it reaches the maximum limit (). Is this correct?

No. Adding 1 to the maximum int value is undefined behavior. There is no should. It might result in a negative number, might not, might exit code - it is not defined.

Had code been

signed char c = 127;
printf("%d\n", c + 1 );

c + 1 would be 128 and "128\n" would be printed as c + 1 is an int operation with an in range int sum.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • just a question. Isn't signed overflow undefined behaviour so wrapping around isn't allowed? – Jerry Jeremiah Jun 08 '21 at 02:04
  • 1
    @JerryJeremiah Signed overflow is undefined behavior, but there is no overflow here. `signed char c = 255;` is not overflow. it is an out-of-range conversion. "Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised." C17dr 6.3.1.3 `c + 1` is `-1 + 1` --> no overflow. – chux - Reinstate Monica Jun 08 '21 at 02:07
  • Thanks for that. I knew there was no overflow, but his code example was different than his question. He said "I am trying to add '1' to a character which is holding maximum positive value it can hold. ... As per my understanding, it should give negative numbers once it reaches the maximum limit" Which clearly (to me at least) means he thinks that you can do overflow and expect it to work. And, of course, the code example implies that 255 is the largest value a signed char can hold - but that wasn't really his question so I just wanted to make sure.... – Jerry Jeremiah Jun 08 '21 at 02:13
  • 2
    @JerryJeremiah And even if `c` were initialized to 127, `c + 1` would not result in signed overflow. The value of `c` would still be promoted to `int`, and `c + 1` would yield the `int` value 128. There are no arithmetic operations on integer types narrower than `int` and `unsigned int`. Values of any narrower types are promoted before the arithmetic operator is applied. – Keith Thompson Jun 08 '21 at 02:14
  • @KeithThompson Thanks. That's what I was trying to figure out. I really appreciate that. – Jerry Jeremiah Jun 08 '21 at 02:15
1

There's several implicit conversions to keep track of here:

  • signed char c = 255; Is a conversion of the constant 255 which has type int, into a smaller signed char. This is "lvalue conversion through assignment" (initialization follows the rules of assignment) where the right operand gets converted to the type of the left.

    The actual conversion from a large signed type to a small signed type follows this rule:

    Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

    In practice, the very likely conversion to happen on a two's complement computer is that you end up with the signed char having the decimal value equivalent to 0xFF, which is -1.

  • c + 1 is an operation with two operands of types signed char and int respectively. For the + operator, it means that the usual arithmetic conversions are performed, see Implicit type promotion rules.

    Meaning c gets converted to int and the operation is carried out on int type, which is also the type of the result.

  • printf("%d\n", stuff ); The functions like printf accepting a variable number of arguments undergo an oddball conversion rule called the default argument promotions. In case of integers, it means that the integer promotions (see link above) are carried out. If you pass c + 1 as parameter, then the type is int and no promotion takes place. But if you had just passed c, then it gets implicitly promoted to int as per these rules. Which is why using %d together with character type actually works, even though it's the wrong conversion specifier for printing characters.


As per my understanding, it should give negative numbers once it reaches the maximum limit (). Is this correct?

If you simply do signed char c = 127; c++; then that's a signed overflow, undefined behavior with no predictable outcome.

If you do signed char c = 127; ... c + 1 then there's no overflow because of the implicit promotion to int.

If you do unsigned char c = 255; c++; then there is a well-defined wrap around since this is an unsigned type. c will become zero. Signed types do not have such a well-defined wrap around - they overflow instead.

In practice, signed number overflow is artificial nonsense invented by the C standard. All well-known computers just set an overflow and/or carry bit when you do overflow on assembler level, properly documented and well-defined by the core manual. The reason it turns "undefined behavior" in C is mainly because C allows for nonsensical signedness formats like one's complement or signed magnitude, that may have padding bits, trap representations or other such exotic, mostly fictional stuff.

Though nowadays, optimizing compilers take advantage of overflow not being allowed to happen, in order to generate more efficient code. Which is unfortunate, since we could have had both fast and 100% deterministic code if 2's complement was the only allowed format.

Lundin
  • 195,001
  • 40
  • 254
  • 396