如何在GCC中收到有关无符号整数溢出而不是环绕的警告?

时间:2019-02-23 02:38:38

标签: c gcc gcc-warning

测试ENV

  • Linux
  • 英特尔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。

2 个答案:

答案 0 :(得分:3)

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

对于无符号整数,将发生溢出,就好像值是对给定类型的最大值进行模运算后得出的。换句话说,如果类型为n位宽,则仅保留结果的低阶n位。这实际上不是溢出,而是称为环绕

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

  

有符号整数的非负值范围
  type是相应的无符号整数的子范围   类型,以及每种类型中相同值的表示形式   是一样的涉及无符号操作数的计算可以   永远不会溢出,因为结果无法用   所得的无符号整数类型将以   大于最大值的数字   由结果类型表示。

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

在第二个示例中:

long x = 2147483647U * 3U; 

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

答案 1 :(得分:0)

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

例如,示例1和2都将由MISRA C检查器标记, 因为这些表达式在常量中溢出-这表明程序员犯了错误。 2012 Rule 12.4:“常量表达式的求值不应导致无符号整数的回绕。”

INT30-C在SEI CERT C编码标准中描述了一个更通用的情况,该建议建议避免对安全应用程序进行任何形式的溢出,并提供遵循该规则的自动检查程序列表。