13.2.4针对已签名溢出问题的实用建议

理想情况下,最安全的方法是避免有符号整数溢出完全。例如,不是将两个有符号整数相乘,而是可以将它们转换为双宽整数,将更宽的值相乘,然后测试结果是否在较窄的范围内。或者您可以使用使用相同宽度的无符号整数的更复杂的代码。

不过,以这种方式重写代码将很不方便,特别是如果带符号的值可能是负值,并且没有更宽的类型可用。使用无符号算法检查溢出是在处理整数类型类似uid_(t)其宽度和标志与平台到平台。此外,这种方法可能会影响性能。

因此,维护需要溢出时进行包装,而不是重写代码。剩下的本节试图针对这种情况提出切实可行的建议。

尝试以下操作时可移植地检测整数溢出总和=a+b,您可以使用C23<stdckdint.h>ckd添加,ckd订阅、和ckd_mul公司.以下代码添加了两个带溢出包装的整数总额可靠:

#包括<stdckdint.h>.../*设置sum=a+b,带环绕*/if(ckd_add(&sum,a,b))/*“sum”只有低位。*/;其他的/*“sum”是正确答案。*/;

要便携到C23之前的平台,您可以使用Gnulib标准ckdint模块,它模拟C23的这一部分(请参见格努利布).调用标准ckdint宏通常只需要一台机器算术指令和另一条稀有指令溢出上的分支。

如果代码使用带符号的循环索引,请确保该索引不能溢出,以及从索引派生的所有有符号表达式。下面是一个有问题的代码的人为示例,其中有两个实例溢出。

对于(int i=int_MAX-10;i<=int_MAX;i++)如果(i+1<0){报告溢出();断裂;}

由于这两个溢出,编译器可能会进行优化,或者以与包罗万象的假设。

如果您的代码仅由GCC编译,并且假设有环绕行为,您希望将其隔离不支持任何GCC优化行为,您应该使用GCC-fwrapv公司选项,其中导致签名溢出可靠地环绕(除法和剩余部分,如下一节所述)。

如果您需要编写可移植代码,因此无法假定有符号整数溢出可以可靠地进行包装,您应该考虑使用GCC选项进行调试,该选项会导致签名溢出引发例外。这些选项包括-fsanitize=未定义-ftrapv公司.