3

How C++ handles cout of negative value of signed char? Is the behavour defined in C++11 standard? I am using MinGW C++ 11 compiler. It looks the signed value is converted to unsigned type by adding 256 and then prints extended ASCII characters.

signed char a=-35;
std::cout<<a;
Rajesh
  • 1,085
  • 1
  • 12
  • 25

3 Answers3

5

According to this, the following overload is selected:

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
                                        signed char ch );

And since signed char is not char, a is first converted to a char using widen:

char_type widen( char c ) const;

So your code is equivalent to:

std::cout << std::cout.widen(c);
// or:
std::cout << std::use_facet< std::ctype<char> >(getloc()).widen(c)

As you can see, widen takes a char, so you'll have a conversion from signed char to char prior to the actual "widening".

Even if you are widening from a char to a char, the behavior is implementation-defined — The standard makes no guarantee regarding this.

Holt
  • 36,600
  • 7
  • 92
  • 139
  • char in this compiler seems to be signed since cout << numeric_limits::is_signed; returns me 1. So How it is printing extended ASCII characters (128 to 255)? For extended ASCII character, it should be converted to unsigned value right? – Rajesh Jan 16 '18 at 08:04
  • @Rajesh You are assuming that the character set is ASCII + extended ASCII but the standard makes no assumption regarding this. On my computer, I just get a `?`. What's display on your terminal is dependent on what you terminal encoding is. The program might write `-35` and you system interpret it as `221`. – Holt Jan 16 '18 at 08:41
  • Having some difficulty in understanding statement "The program might write -35 and you system interpret it as 221". Sorry, I have some issue and hope you can elaborate. I ran below code and both produce same output. So -35 becomes 221 according to my compiler. If it were to become signed char, a value would never go beyond 127 and hence extended characters would never print right? signed char a; for(int cnt=-1;cnt>=-128;cnt--) { a=cnt; std::cout<=128;cnt--) { printf("%c\t",cnt); } – Rajesh Jan 16 '18 at 10:39
  • 1
    @Rajesh Sorry this was poorly written. Your code will likely write the binary representation of `-35` to the stream associated with `std::cout`. Now, how this binary value is interpreted on the other side is dependent on your system. Your system might read the binary representation, interpret it as 221, and display the corresponding character in the character set defined in your terminal. – Holt Jan 16 '18 at 10:45
3

Use type casting to int...

std::cout << (int)a;

...or, following better C++ programming style (as Christian Hackl suggested):

std::cout << static_cast<int>(a);

This actually does not answer your questions (already been answered by Holt), but shows a solution to the problem.

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
2

Most of this is required behavior (and most of what isn't still borders on being required).

To be specific, the C++ standard says that iostreams are associated with C-style input and output streams, so cout is associated with stdout (§[narrow.stream.objects]/3):

The object cout controls output to a stream buffer associated with the object stdout, declared in <cstdio>.

The C standard, in turn, defines narrow-character output as being as-if written via fputc (§7.19.3/12):

The byte output functions write characters to the stream as if by successive calls to the fputc function.

fputc requires (§7.19.7.3/2):

The fputc function writes the character specified by c (converted to an unsigned char) to the output stream pointed to by stream, [...]

So, yes, the conversion to unsigned char is exactly what the standards require. The C standard requires that conversion from signed to unsigned (of any integer type, including char) happen in the following fashion (§6.3.1.3/2):

Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

So yes, it is converted to unsigned by adding 256 (assuming unsigned char can represent values from 0 to 255, as is typical).

So that leaves us one part that the standard sort of attempts to require, without going quite all the way--the transformation that widen has to do (§[locale.ctype.virtuals]/10):

Applies the simplest reasonable transformation from a char value or sequence of char values to the corresponding charT value or values.

Since it's a little difficult to decide exactly what's "reasonable", this could carry out some more or less arbitrary mapping on your character. In fact, it's apparently mapping input to output without modification (at least for the particular character you're writing), but it's true that other transformations could fall within "reasonable", and it would ultimately be difficult to draw a hard line saying that any particular transformation was not "reasonable".

The other part that's not really required by the C++ (or any other) standard is how something else will interpret that output. All the language standards can mandate is what gets written to the stream. The part with something else opening that stream and interpreting its content as "extended ASCII" (probably one of the ISO 8859 variants) is clearly outside the control of the language (or much of anything else in your program, of course).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • You are quoting the C standard but where does the C++ standard says that output to `std::cout` are managed by call to `fputc`? Because `std::cout` is associated with `stdout` does not mean that all output to `std::cout` are managed with `fputc` as there are for `stdout` in C. The only thing I can find in the C++ standard is related to [`basic_streambuf::sputc`](http://en.cppreference.com/w/cpp/io/basic_streambuf/sputc) which takes a `char` and not an `unsigned char`. – Holt Jan 16 '18 at 08:36
  • @Holt: the C++ standard just says the data is written to stdout. The C standard says all byte output (corresponds to what the C++ standard calls narrow character output) is as-if by fputc. I've edited to add the quote from the C standard addressing that specific point. Since the C++ stream writes (as if) to `stdout` and all output to `stdout` is written (as if) via `fputc`, all output from C++ must be (as if) via `fputc`. – Jerry Coffin Jan 16 '18 at 14:33
  • The quote starts with *"The byte output functions"*, so as I understand it this would mean that all C functions write to the stream as-if by `fputc`, but this does not imply that C++ functions do. As I read your answer (maybe I am missing something), I understand *"`std::cout` and `stdout ` use the same output buffer, C operations on `stdout` are as-if via `fputc`, so operations on `std::cout` must also be."*, but this does not seem right. – Holt Jan 16 '18 at 14:36
  • @Holt: If it meant: "cout and stdout use the same output buffer", that's what it would say. That's **not** what it says or requires though. What it says and requires is that it's associated with `stdout`, not just with the buffer that `stdout` controls. Since `cout` is an instantiation over `char`, the `charT` for `cout` is `char`. Therefore, `cout` writes bytes to `stdout`, which (repeating here) has to be done as if by `fputc`. I realize you don't want to admit that your answer is mostly wrong, but you're starting to contort the language beyond recognition to protect your mistake. – Jerry Coffin Jan 16 '18 at 16:45
  • Ok, I misunderstood this part, but still *"The byte output functions"* are a strict subset of the C I/O library functions (fprintf, fputc, fputs, fwrite, printf, putc, putchar, puts, vfprintf, vprintf in my version), and I the C++ standard does not require (unless I missed it) that the implementation-defined `streambuf` associated with `stdout` and used by `std::cout` have to use these functions to write to `stdout`, it may use low-level routines. – Holt Jan 16 '18 at 16:59
  • @Holt: What "low-level routines" are you referring to? According to the C standard, "The input/output functions are given the following collective terms". That list includes only: "wide character input functions", "wide character output functions", "wide character input/output functions", and "byte input/output functions". That's an exhaustive list so unless you believe `cout` is somehow going to use wide character output functions, the only possibility is for it to use byte input/output functions. – Jerry Coffin Jan 16 '18 at 17:08
  • libstdc++ uses [`write`](https://linux.die.net/man/2/write), it's implementation defined how the `streambuf` associated with `std::cout` writes to `stdout`. – Holt Jan 16 '18 at 17:24
  • @Holt: Implementation can always vary under the "as if" rule. The *behavior* is fully defined though--it writes `char`s to `stdout`. It has to act as if it were using C's byte input/output functions, because that's how `stdout` is defined. That means it has to act as if all the output went through `fputc`, which means it has to be converted to unsigned as already described at nauseating length. Why do I get the feeling that your next line is going to be something about "that depends on what the definition of 'is' is." – Jerry Coffin Jan 16 '18 at 19:11
  • That depends on what the definition of 'is' is... Joke inside, I am not trying to be a pain, I really have a hard time understanding the logic in your answer, (for me) there is something missing. I am not saying it is wrong, I might be missing something trivial, but still... I'll try to look at it after a good night and see if it makes sense tomorrow. – Holt Jan 16 '18 at 20:05