如何在 GCC 中获得无符号整数溢出而不是环绕的警告?

How to get a warning in GCC for unsigned integer overflow instead of wrap-around?

测试环境

示例 1,收到警告,好:

long x = 2147483647 * 3;

示例 2,没有警告,不好:

long x = 2147483647U * 3U; // Suffix U

unsigned int a = 2147483647;
unsigned int b = 3;
long x = a*b;

示例 3,没有警告,但按预期工作:

long x = 2147483647L * 3L; // Suffix L

在例子2中,我知道它是一个循环而不是整数溢出,但这些是编译器无法警告的情况?

来自标准:

(6.3.1.8)

Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:

If both operands have the same type, then no further conversion is needed. Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

(6.5):

If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its (type), the behavior is undefined.


开始使用带有标志 -fsanitize=unsigned-integer-overflow 的 Clang,这对处理回绕产生的不需要的值有很大帮助。这不是整数溢出,但不是预期值。由于 GCC,直到现在不支持这样的警告,继续使用 Clang。

有符号整数溢出调用undefined behavior,而无符号整数溢出定义明确。

对于无符号整数,溢出的发生就好像这些值的计算模数比给定类型的最大值大一。换句话说,如果类型是 n 位宽,则只保留结果的低位 n 位。这实际上不是溢出,而是被称为 wraparound.

这在 section 6.5p9 中有详细说明:

The range of nonnegative values of a signed integer
type is a subrange of the corresponding unsigned integer type, and the representation of the same value in each type is the same. 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 type.

由于此行为定义明确,编译器触发警告没有意义。

对于你的第二个例子:

long x = 2147483647U * 3U; 

乘法是对unsigned类型进行的,所以数学结果6442450941回绕到2147483645,在long的范围内。没有溢出(只是回绕)也没有超出范围的转换,所以没有警告。

不是 GCC,但一些静态分析器规则会警告此类“溢出”。

例如,示例 1 和 2 都会被 MISRA C 检查器标记, 因为这些在带有常量的表达式中溢出——这表明程序员犯了错误。 2012 规则 12.4:“常量表达式的计算不应导致无符号整数 wrap-around。”

SEI CERT C 编码标准中的 INT30-C 描述了一个更通用的案例,它建议避免任何类型的安全应用程序溢出,并提供遵守该规则的自动检查器列表。