C-如何在GCC中发出警告,以获取未签名的整数溢出而不是包裹



测试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):

如果在评估表达式期间发生特殊条件(即,如果结果未定义或不在其(类型)的可表示值范围内定义,则该行为是未定义的。


开始将clang与flag -fsanitize=unsigned-integer-overflow一起使用,这对包裹环绕中的不想要的值很有帮助。那不是整数溢出,而是预期的值。由于GCC,到目前为止不支持这样的警告,转到Clang。

签名整数的溢出调用不确定的行为,而无签名的整数溢出是充分定义的。

对于无符号整数,溢出的发生似乎比给定类型的最大值要多得多。换句话说,如果类型为n位宽,则仅保留结果的低阶n位。这实际上不是溢出,而是称为 wraparound

这是在第6.5p9节中阐明的:

签名整数的非负值范围
类型是相应的未签名整数的子范围 类型,以及每种类型中相同值的表示 是一样的。涉及未签名操作数的计算可以 永远不要溢出,因为结果无法代表 由此产生的未签名整数类型减少了模型 数字大于最大值的数字 由结果类型表示。

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

在您的第二个示例中:

long x = 2147483647U * 3U; 

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

不是GCC,但是某些静态分析仪规则会警告"溢出"。

例如,两个示例1&2将由Misra C Checker标记因为这些用常数在表达中溢出 - 这表明程序员错误。2012年规则12.4:"评估常数表达式不应导致未签名的整数包裹。"

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

最新更新