It seems clear that the following code invokes undefined behavior because of arithmetic overflow:
#include <limits.h>
int test(void) {
int i = INT_MAX;
i++; /* undefined behavior */
return i;
}
But what about signed types smaller than int
such as short
or signed char
? (by smaller, I assume SCHAR_MAX < INT_MAX
and SHRT_MAX < INT_MAX
respectively).
Which of the functions below invoke undefined behavior and why?
signed char test2(void) {
signed char i = SCHAR_MAX;
i = i + 1; /* implementation defined */
return i;
}
signed char test3(void) {
signed char i = SCHAR_MAX;
i += 1; /* undefined behavior or implementation defined? */
return i;
}
signed char test4(void) {
signed char i = SCHAR_MAX;
i++; /* undefined behavior or implementation defined? */
return i;
}
signed char test5(void) {
signed char i = SCHAR_MAX;
++i; /* undefined behavior or implementation defined? */
return i;
}
Please provide references from or quote the C Standard to support your reasoning.
12.2. 1 Basics of Integer Overflow In contrast, the C standard says that signed integer overflow leads to undefined behavior where a program can do anything, including dumping core or overrunning a buffer. The misbehavior can even precede the overflow.
"Signed integer overflow" means that you tried to store a value that's outside the range of values that the type can represent, and the result of that operation is undefined (in this particular case, your program halts with an error).
-fsanitize=unsigned-integer-overflow : Unsigned integer overflow, where the result of an unsigned integer computation cannot be represented in its type. Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional.
Use 64-bits integers. One very good way to prevent integer overflows is to use int64_t to implement integers. In most case, 64-bits ints will not commit overflow, unlike their 32-bits counterparts. There is actually very few downsides in using int64_t instead of int32_t .
It would be logical for the +=
and similar operators to operate directly on values of the destination type, and on many implementations that is in fact what they do. The Standard, however, requires that the operator behave as though the value of the destination undergoes any applicable standard and balancing promotions, then processes the specified operation, and then gets converted back to the destination type.
Consequently, if s
and u
are signed and unsigned 16-bit variables, and int
is 32 bits, then s*=s;
will be defined for all values of s
[if the result exceeds 32767, it must either yield an implementation-defined value or raise an implementation-defined signal; most implementations would have to go out of their way to do anything other than two's-complement reduction]. On the other hand, u*=u;
will only be guaranteed to wrap mod 65536 for values of u
up to 46340; for larger values, it will invoke Undefined Behavior.
The following code does cause and overflow and thus undefined behavior:
signed char i = SCHAR_MAX;
i++;
The operator postfix ++ does not perform integer promotions or usual arithmetic conversions1. The operator simply adds one to the value of the operand2. The operations overflows.
A clear distinction comes from the wording of the unary arithmetic operators: +
, -
, ~
, for which the wording clearly says that the operand is promoted3. But for the postfix ++
operator the wording doesn't say that the operator is promoted4.
Clearly the postfix ++ operator does not promote the operand.
1 (Quoted from: ISO/IEC 9899:201x 6.3.1.1 Boolean, characters, and integers 2 58))
The integer promotions are applied only: as part of the usual arithmetic conversions, to certain
argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the
shift operators, as specified by their respective subclauses.
2 (Quoted from: ISO/IEC 9899:201x 6.5.2.4 Postfix increment and decrement operators 2)
As a side effect, the
value of the operand object is incremented (that is, the value 1 of the appropriate type is
added to it)
3 (Quoted from: ISO/IEC 9899:201x 6.5.3.3 Unary arithmetic operators 2)
The result of the unary + operator is the value of its (promoted) operand. The integer
promotions are performed on the operand, and the result has the promoted type.
4 (Quoted from: ISO/IEC 9899:201x 6.5.2.4 Postfix increment and decrement operators 2)
The result of the postfix ++ operator is the value of the operand.
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