For example:
size_t x;
...
__builtin_uaddll_overflow(x,1,&x);
Would the above code correctly guard against integer overflow regardless of compiler implementation?
What I know so far:
size_t
is an unsigned type.typedef unsigned long size_t;
may be used to define size_t
.Is there a function listed at this reference that will always be correct? Or will it necessarily depend on the specific implementation? If so, how could I programatically choose the correct function?
size_t x;
...
__builtin_uaddll_overflow(x,1,&x);
Would the above code correctly guard against integer overflow regardless of compiler implementation?
No. __builtin_uaddll_overflow()
is not a specified C operator nor a C standard library function. It restricts code to select compilers. The functionality of __builtin_uaddll_overflow()
is not specified by C.
Instead, simply compare against SIZE_MAX
for a portable implementation. It is portable regardless if size_t
is the same as unsigned
, unsigned long
or some other unsigned type, even wider or narrower than unsigned
.
size_t x;
size_t y;
if (SIZE_MAX - x < y) Overflow();
else size_t sum = x + y;
This works for the various unsigned types too - even narrow ones. Use the same some_unsigned_type
throughout.
some_unsigned_type x;
some_unsigned_type y;
if (some_unsigned_type_MAX - x < y) Overflow();
else some_unsigned_type sum = x + y;
When working with unsigned types at least as wide as unsigned
, code could use the following.
some_at_least_unsigned_type x;
some_at_least_unsigned_type y;
some_at_least_unsigned_type sum = x + y; // overflow behavior well defined
if (sum < x) {
Overflow();
}
else {
// continue with non-overflowed sum
}
In the case of size_t
, size_t
is very commonly as wide or wider than unsigned
, although not specified to be so.
The above usually works with unsigned types narrower then unsigned
. Yet the x + y
is done with int
math then and unicorn platforms could then overflow int
math. 1u*x + y
or 0u + x + y
forces the math to always be done as unsigned, regardless if some_unsigned_type
is narrower/wider than unsigned
. A good compiler will emit optimize code that does not perform an actual 1u*
multiplication.
some_unsigned_type x;
some_unsigned_type y;
some_unsigned_type sum = 1u*x + y; // overflow behavior well defined
if (sum < x) {
Overflow();
}
else {
// continue with non-overflowed sum
}
Or in OP's + 1 example, to insure unsigned math addition:
size_t x;
size_t sum = x + 1u; // overflow behavior well defined
if (sum == 0) {
Overflow();
}
else {
// continue with non-overflowed sum
}