There has been no significant change in this area from C90 to C99, or from C99 to C11.
C90 6.1.2.5 (there are no paragraph numbers, but it's the first paragraph on page 23 of the PDF) says:
A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type.
Nearly the same wording (with the phrase "resulting unsigned integer type" changed to "resulting type" appears in 6.2.5 paragraph 9 in the C99 and C11 standards.
The only significant change (that I'm aware of) is an addition in C99 regarding signed conversion. In C90, if a signed or unsigned value is converted to a signed integer type, and the result cannot be represented in the target type, the result is implementation-defined. In C99 and C11, either the result is implementation-defined or an implementation-defined signal is raised. (I don't know of any compiler that takes advantage of the permission to raise a signal.)
As supercat points out in a comment, multiplication of two unsigned short
values can overflow. Suppose short
is 16 bits, int
is 32 bits, and integer representations are typical (2's-complement for signed types, no padding bits). Then given:
unsigned short US = USHRT_MAX; // 65536
both operands in the expression
us * us
are promoted from unsigned short
to int
(because int
can hold all possible values of type unsigned short
) -- but the product, which is nearly 232, cannot fit in a 32-bit signed int
.
(During the C standardization process in the late 1980s, the committee had to choose between "value-preserving" and "unsigned-preserving" integer promotions. They chose the former. With unsigned-preserving semantics, the unsigned short
operands would be promoted to unsigned int
, and this problem would not occur.)