I have the following C code:
uint8_t firstValue = 111;
uint8_t secondValue = 145;
uint16_t temp = firstValue + secondValue;
if (temp > 0xFF) {
return true;
}
return false;
This is the alternative implementation:
uint8_t firstValue = 111;
uint8_t secondValue = 145;
if (firstValue + secondValue > 0xFF) {
return true;
}
return false;
The first example is obvious, the uint16_t
type is big enough to contain the result.
When I tried the second example with the clang
compiler on OS/X, it correctly returned true. What happens there? Is there some sort of temporary, bigger type to contain the result?
The operands of +
are promoted to larger types, we can see this by going to draft C99 standard section 6.5.6
Additive operators which says:
If both operands have arithmetic type, the usual arithmetic conversions are performed on them.
and if we go to 6.3.1.8
Usual arithmetic conversions it says:
Otherwise, the integer promotions are performed on both operands.
and then we go to 6.3.1.1
Boolean, characters, and integers which says (emphasis mine):
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.48) All other types are unchanged by the integer promotions.
So both operands of +
in this case will be promoted to type int for the operation, so there is no overflow.
Note, Why must a short be converted to an int before arithmetic operations in C and C++? explains the rationale for promotions.
The first example is obvious, the uint16_t type is big enough to contain the result.
In fact the destination lvalue x
for the assignment x = expr;
has no bearing on whether there is an overflow in expr
. If there is, then the result is what it is regardless of how wide x
is.
In your example, “integer promotions” apply and the computation is done between int
operands. This means that there is no overflow. Integer promotions are described in C11 in clause 6.3.1.1:2.
If you had been adding two uint32_t
values, then there could have been wrap-around (the specified behavior when an unsigned operation produces a result that is out of bounds for the unsigned type), even if the type of the lvalue to assign to result to was uint64_t
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With