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

问题描述 投票:2回答:1

测试ENV

  • Linux的
  • Intel x86-64 GCC 8.2.1
  • 启用标志:-Wextra -Wall -Wfloat-equal -Wundef -Wshadow -Winit-self -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wstrict-overflow=5 -Wwrite-strings -Waggregate-return -Wcast-qual -Wswitch-default -Wswitch-enum -Wconversion -Wunreachable-code -Wformat=2 -pedantic -pedantic-errors -Werror-implicit-function-declaration -Wformat-security -fstrict-overflow
  • sizeof(long)是8。
  • sizeof(int)是4。

示例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)

否则,将对两个操作数执行整数提升。然后将以下规则应用于提升的操作数:

如果两个操作数具有相同的类型,则不需要进一步转换。否则,如果两个操作数都具有有符号整数类型或两者都具有无符号整数类型,则具有较小整数转换等级类型的操作数将转换为具有更高等级的操作数的类型。

否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则具有有符号整数类型的操作数将转换为具有无符号整数类型的操作数的类型。

否则,如果带有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数将转换为带有符号整数类型的操作数的类型。

否则,两个操作数都转换为无符号整数类型,对应于带有符号整数类型的操作数的类型。

(6.5):

如果在计算表达式期间发生异常情况(即,如果结果未在数学上定义或未在其(类型)的可表示值范围内,则行为未定义。


开始使用带有标志-fsanitize=unsigned-integer-overflow的Clang,它可以帮助很多来自环绕的不需要的值。这不是整数溢出,而是预期值。自GCC以来,直到现在都不支持这样的警告,继续前往Clang。

c gcc gcc-warning
1个回答
3
投票

有符号整数的溢出调用undefined behavior,而无条件整数溢出被很好地定义。

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

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

有符号整数的非负值范围 type是相应无符号整数类型的子范围,每种类型中相同值的表示形式相同。涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模。

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

在你的第二个例子的情况下:

long x = 2147483647U * 3U; 

乘法在unsigned类型上完成,因此数学结果6442450941回绕到2147483645,这在long的范围内。没有溢出(只是环绕)并且没有超出范围的转换,所以没有警告。

热门问题
推荐问题
最新问题