Random832 said:
Masking. And with the fact that overflows are undefined anyway.
Well, that's my point: You (probably) cannot work directly
with the operands in their original sizes, but must first convert
them to some other form and work with that other form. That's
exactly C's model: Convert a "small" operand to a "large" equivalent,
perform "large" arithmetic, and convert the result to "small" again.
I really, truly, cannot see why you think it "ridiculous."
There's no reason, if c is type unsigned char, why ~c should not also be
of type unsigned char.
On a machine with a char-sized accumulator you might be right.
But not all machines have such things, and those whose narrowest
arithmetic is wider than char would incur performance penalties
when evaluating expressions with narrow operands. (A naive code
generator might just insert an AND after each operation; a smarter
one might be able to eliminate some of them.)
Even so, you still need promotion rules to handle expressions
with mixed operand widths -- and as long as you've got to have
promotion rules anyhow, why not use them to simplify arithmetic
elsewhere, too?
Remember: The design of C did not drive the design of the
machines it ran on; 'twas the other way about.
And there's no reason signedness shouldn't be preserved, though it
didn't matter in the OP's case.
The ANSI Committee discussed the issue of value-preserving vs.
signedness-preserving promotions at some length, and (one suspects)
with some vigor. There's a fairly extensive summary of the positions
and their consequences in the Rationale.