cppreference.com states that char is
Equivalent to either
signed charorunsigned char[...], butcharis a distinct type, different from bothsigned charandunsigned char
I assume this means that a char can hold exactly the same values as either unsigned char or signed char, but is not compatible with either. Why was it decided to work this way? Why does unqualified char not denote a char of the platform-appropriate signedness, like with the other integer types, where int denotes exactly the same type as signed int?
TL;DR
Backwards compatibility. Probably. Or possibly that they had to choose and didn't care. But I have no certain answer.
Long version
Intro
Just like OP, I'd prefer a certain answer from a reliable source. In the absence of that, qualified guesses and speculations are better than nothing.
Very many things in C comes from backwards compatibility. When it was decided that whether
charwould be the same assigned charorunsigned charis implementation defined, there were already a lot of C code out there, some of which was using signed chars and others using unsigned. Forcing it to be one or the other would for certain break some code.Why it (probably) does not matter
It does not matter much. An an implementation that is using signed chars guarantees that
CHAR_MINis equal toSCHAR_MINand thatCHAR_MAXis equal toSCHAR_MAX. Same goes for unsigned. So an unqualifiedcharwill always have the exact same range as its qualified counterpart.From the standard 5.2.4.2.1p2:
This points us in the direction that they just didn't really care, or that it "feels safer".
Another interesting mention in the C standard is this:
Possible problems with breaking this (speculation)
I'm trying to come up with a scenario where this would actually matter. One that could possibly cause issues is if you compile a source file to a shared library with one compiler using signed char and then use that library in a source file compiled with another compiler using unsigned char.
And even if that would not cause problems, imagine that the shared library is compiled with a pre-ansi compiler. Well, I cannot say for certain that this would cause problems either. But I can imagine that it could.
And another speculation from Steve Summit in comment section:
Backwards compatibility is a sacred feature
But remember that the persons behind the C standard were and are VERY concerned about not breaking backwards compatibility. Even to the point that they don't want to change the signature of some library functions to return
constvalues because it would yield warnings. Not errors. Warnings! Warnings that you can easily disable. Instead, they just wrote in the standard that it's undefined behavior to modify the values. You can read more about that here: https://thephd.dev/your-c-compiler-and-standard-library-will-not-help-youSo whenever you encounter very strange design choices in the C standard, it's a very good bet that backwards compatibility is the reason. That's the reason why you can initialize a pointer to
NULLwith just0, even for a machine where NULL is not the zero address. And whyboolis a macro of the keyword_Bool.It's also the reason why bitwise
|and&has higher precedence than==, because there were a lot (several hundred kilobytes that was installed on three (3) machines :) ) of source code including stuff likeif (a==b & c==d). Dennis Ritchie admitted that he should have changed it. https://www.lysator.liu.se/c/dmr-on-or.htmlSo we can at least say for certain that there are design choice made with backwards compatibility in mind, that has later been admitted by those who made the choices to be mistakes and that we have reliable sources for that.
C++
And also remember that your sources points to C++ sources. In that language, there are reasons that don't apply to C. Like overloading.