4

Consider this example:

#include <iostream>
int main()
{
    char c = 256;
    std::cout << static_cast<int>(c);
}

Raise a warning:

warning: overflow in conversion from 'int' to 'char' changes value from '256' to ''\000'' [-Woverflow]

But this:

#include <iostream>
int main()
{
    char c = 255;
    std::cout << static_cast<int>(c);
}

don't, but the std::cout in both cases doesn't print 256 and 255, so it shows char can't hold 256 and 255, but the warning only raise when the char c is 256?

You can toy around with it here

  • 3
    An unsigned char has a range of 0 .. 255. 255 is valid if your `char` is unsigned – drescherjm Dec 30 '21 at 03:46
  • 1
    ... and any signed integer overflow makes your program have _undefined behavior_. – Ted Lyngmo Dec 30 '21 at 03:47
  • 1
    In your godbolt example, try adding "-funsigned-char" and you'll see the expected behaviour https://stackoverflow.com/questions/2054939/is-char-signed-or-unsigned-by-default – Martheen Dec 30 '21 at 03:50
  • Because warning something or not warning and working like you expect or do not expect all fit well into range of undefined behavior. – Öö Tiib Dec 30 '21 at 04:59
  • 1
    @TedLyngmo There is no undefined behavior in these conversions. Since C++20 it is well-defined and before it was implementation-defined. – user17732522 Dec 30 '21 at 05:17
  • @drescherjm In the case of the example linked in the question the `char` is signed. – user17732522 Dec 30 '21 at 05:17
  • @user17732522 Yes. What happens if you read such a variable without a definition of what happens when you read it? (before c++20) They kept the UB when overflowing signed integers even in C++20 btw. – Ted Lyngmo Dec 30 '21 at 05:19
  • 1
    @TedLyngmo Implementation-defined is supposed to mean that the compiler should behave consistently in a documented manner. It should produce some value, only how it is produced may vary. In contrast undefined behavior means that all guarantees on the program are lost. Yes, overflowing in arithmetic is still UB, but not in conversion. – user17732522 Dec 30 '21 at 05:25
  • @user17732522 Indeed. It should be documented. It does not mean, do whatever and it's fine because somewhere someone has defined that there is a behavior it'll fit. – Ted Lyngmo Dec 30 '21 at 05:27
  • Assigning [-128-127] is not an issue for _signed_ `char`. Assigning outside that range may be one. Assigning [128-255] is less likely to be an issue to warn about. I suspect the compiler is simply not waring about this less likely issue. – chux - Reinstate Monica Dec 30 '21 at 05:28
  • Btw, I think this question is well asked. :-) – Ted Lyngmo Dec 30 '21 at 05:32

1 Answers1

6

It is important to specify whether char is signed in your example or not, but going by your link it is signed.

If you write

char c = 256;

256 has type int, so to store the value in c a conversion to char has to happen.

For signed target types such integer conversions produce the same value if it is representable in the target type. That would with the typical bit size and representation of a signed char be -128 to 127.

What happens if the source value is not representable in the target type depends on a few factors.

First, since C++20 two's-complement is guaranteed, meaning that the resulting value is guaranteed to be the unique value such that the it and the source value are equal modulo 2^n with n the bit size of the target type (typically 8 for char).

Before C++20 it was implementation-defined what happens in such a case, but it is very likely that the implementation would just have specified behavior equivalent to the C++20 one.

So, the warning is not meant to prevent undefined behavior or even implementation-defined behavior since C++20, but just to notify the user about likely mistakes.

I can't be sure why GCC chooses to warn only for values larger than 255, but my guess is that it is done because a statement like

char c = 255;

makes sense if you interpret the right-hand side as an unsigned char. The conversion rule explained above would not change the character represented before and after the conversion.

However

char c = 256;

doesn't even make sense if the right-hand side is interpreted as unsigned char. So the likelihood that this is a mistake seems higher.

But maybe I am also guessing in the wrong direction. There is an open bug report for GCC concerning this behavior of the -Woverflow warning. From reading the comments there it is not clear to me what the reasoning for the behavior was originally.

Clang for example consistently warns about all out-of-range values. So there seem to be different thoughts put into the warnings.

user17732522
  • 53,019
  • 2
  • 56
  • 105