例如:
size_t x;
...
__builtin_uaddll_overflow(x,1,&x);
无论编译器实现如何,上面的代码都能正确防止整数溢出吗?
到目前为止,我所知道的:
- 此引用指出
size_t
是无符号类型。 - 根据此讨论,
typedef unsigned long size_t;
可用于定义size_t
。
此参考中列出的函数是否始终正确?还是必然取决于具体的实现?如果是这样,如何以编程方式选择正确的函数?
size_t x;
...
__builtin_uaddll_overflow(x,1,&x);
无论编译器实现如何,上面的代码都能正确防止整数溢出吗?
不。__builtin_uaddll_overflow()
不是指定的 C 运算符,也不是 C 标准库函数。 它限制代码选择编译器。__builtin_uaddll_overflow()
的功能不是由 C 指定的。
相反,只需与可移植实现的SIZE_MAX
进行比较。 无论size_t
是否与unsigned
、unsigned long
或其他一些无符号类型相同,它都是可移植的,甚至比unsigned
更宽或更窄。
size_t x;
size_t y;
if (SIZE_MAX - x < y) Overflow();
else size_t sum = x + y;
这也适用于各种无符号类型 - 甚至是狭窄的类型。 始终使用相同的some_unsigned_type
。
some_unsigned_type x;
some_unsigned_type y;
if (some_unsigned_type_MAX - x < y) Overflow();
else some_unsigned_type sum = x + y;
当使用至少与unsigned
一样宽的无符号类型时,代码可以使用以下内容。
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
}
在size_t
的情况下,size_t
通常与unsigned
一样宽或宽,尽管没有指定如此。
以上通常适用于比unsigned
更窄的无符号类型。 然而,x + y
已经完成了int
数学,独角兽平台可能会溢出数学int
。1u*x + y
或0u + x + y
强制数学始终作为无符号完成,无论some_unsigned_type
是否比unsigned
窄/宽。一个好的编译器会发出不执行实际1u*
乘法的优化代码。
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
}
或者在 OP 的 + 1 示例中,为了确保无符号数学加法:
size_t x;
size_t sum = x + 1u; // overflow behavior well defined
if (sum == 0) {
Overflow();
}
else {
// continue with non-overflowed sum
}