On two's-complement systems, carry and overflow are distinct concepts. Many such systems are designed to support multi-word arithmetic. A carry will be reported if the arithmetic sum, interpreting the values as unsigned, would exceed the range of the type. An overflow will be reported after any computation where the carry into the upper bit differs from the carry out, but will only be meaningful following the computation of the upper byte.
On an 8-bit system, for example, "int" would typically be two bytes, INT_MAX would be 0x7F:0xFF and -1 would be 0xFF:FF. Addition is performed by adding the two lower bytes, and then adding the two upper bytes with a carry from the lower.
Adding 0xFF to 0xFF yields 0xFE and carry but no overflow (carry both into and out of the upper bit).
Adding 0x7F to 0xFF yields 0x7E with no carry nor overflow (carry neither into nor out of the upper bit).
Such details typically shouldn't matter to a C programmer, since compilers typically provide no way to access the overflow flag, nor any way of ensuring that calculations are performed in a way that would make the flag meaningful at any particular point in the code. Nonetheless, the C89 rationale notes:
C code can be non-portable.
Although it strove to give programmers the opportunity to write truly portable programs, the C89 Committee did not want to force programmers into writing
portably, to preclude the use of C as a “high-level assembler”: the ability to write machine- specific code is one of the strengths of C. It is this principle which largely motivates drawing the distinction between strictly conforming program and conforming program (§4).
Note, btw, that the rationale for making small unsigned types promote to signed int
strongly implies that authors of the C89 expected that something
like the following should be safe on commonplace platforms:
unsigned mul_mod_65536(unsigned short x, unsigned short y)
{ return (x*y) & 0xFFFF; }
Nonetheless, such code may malfunction when processed on commonplace 32-bit platforms when processed by excessively-"clever" optimizing compilers like gcc. There is no reason that machine code generated for such a function should care about whether the value of x*y
exceeds INT_MAX
, since the upper bits of the result are going to get chopped off and the lower bits would be correct in any case. In some cases, however, if gcc knows e.g. that y will be 65535, it may decide that since x*y
"can't" exceed INT_MAX, x
"can't" exceed 0x8000. The authors of the Standard may not have wanted to preclude the possibility of C being used as a high-level assembler, but that doesn't meant that compiler writers share such feeling.