You're supposed to use chars for character data, in which case it's not supposed to matter whether or not the integer representation is signed or unsigned. All you generally do with characters is store, compare for equality, and print. Those should all be safe to do without ever knowing if the underlying integer is considered signed or unsigned.
It's only when you start doing arithmetic on characters, or just want to use char to mean "byte" (or "octet") that it matters, and then it's a very good idea to be specific and say "unsigned char" if that is what you expect.
Yep. In their defense, this was a codebase with a 20 year lineage that ran on Linux, Solaris, HP-UX, AS/400, all variants of Windows including mobile, Irix, and some that I'm probably forgetting, and they had never run into this issue before.
Yes, making any assumption on the basic types in C is very dangerous for portability. That's why you should use typedef'd typed such as uint8_t and friends (defined in stdint.h in C99, but easy to roll your own) instead of 'char'.
(except in cases where you don't care about the signedness or memory size)
My employer had code that ran on about 5 billion different platforms, and my job was to port it to a new one. Everything went fine, except for the weird random crashes that would happen periodically. The idea that a "char" could be an "unsigned char" by default was so far off my radar screen that I didn't figure out what was going on until a few days later when I reluctantly dived into the assembly. (It didn't help that it was a mobile platform with basically 0 support for gdb).
Turned out to be a 3 second fix - pass "-fsigned-char" to gcc. Nowadays in new code I always explicitly declare whether my chars are signed or unsigned.