为什么 C 和 C++ 没有内置方法来检查整数溢出?

Why don't C and C++ have built in ways to check for integer overflows?

本文关键字:检查 整数 溢出 方法 内置 C++ 为什么      更新时间:2023-10-16

为什么C和C++不提供一组实现提供的操作来执行每个提供溢出检查的基本整数操作(例如bool safeAdd(int *out, int a, int b))。

据我所知,大多数指令集都有方法来判断操作是否溢出(例如x86溢出和进位标志),并定义在有符号整数的情况下会发生什么。

因此,编译器是否应该比用C和C++编写代码做得更好,创建更简单、更快的操作?

C和C++遵循"你不需要什么就不需要什么"的核心原则。因此,默认的算术运算不会偏离底层架构中用于算术运算的单个指令。

至于为什么没有一个标准的库函数来添加两个整数并检测溢出,我不能说。首先,该语言似乎将有符号整数溢出定义为未定义的行为:

在C编程语言中,有符号整数溢出会导致未定义的行为,

考虑到有多种方法可以实现有符号整数(一的补码、二的补码等),并且在创建C时,这些体系结构都很流行,这是可以理解的,为什么这是未定义的。如果没有大量关于底层平台的信息,很难实现"safe*"纯C函数。这可以在逐个CPU的基础上进行了解。

尽管如此,这并不意味着不可能。如果有人能为C或C++标准机构找到更安全的溢出助手,并能够了解它们被拒绝的原因,我肯定会感兴趣。

无论如何,在实践中有很多方法可以检测算术溢出,库也可以提供帮助。

可能是因为没有需求。算术溢出是未定义的行为,明确表示允许实现这样做检查。如果编译器供应商认为这样做会卖出更多编译器,他们会的。

在实践中,编译器很难做到这一点比程序员更有效。这相当标准验证所有数字输入范围的过程,范围为您可以证明以后的操作不能溢出。一切都很好程序员这样做是一种习惯。所以这意味着一个快速的if在输入之后立即进行,并且没有进一步的检查。

尽管如此,众所周知,程序员会犯错误稍后更改计算时,请忘记更正验证。我希望在编译器中看到这样的功能。但显然不会帮助销售编译器,或者至少供应商认为不会,所以我们不明白。

这个问题经常出现。

首先,请记住,C被定义为可移植高效。因此,它被设计为只提供由大量硬件支持的操作(可能在x86出现之前)。

其次,许多编译器为此类操作提供(或计划提供)内置程序,这样用户就可以在后台使用这些内置程序的类类型。内置程序实现的质量不如编译器意识到它们的意义可以在可证明无用时优化检查这一事实重要(尽管它是重要的)。

最后,还有其他方法可以实际检查程序。例如,静态分析或特殊编译模式&单元测试可以尽早发现这些缺陷,并避免(或多或少完全)在Release构建中嵌入这些溢出检查。

一个更好的问题可能是:为什么整数溢出是未定义的行为?在实践中,99.9%的CPU使用2的补码和进位/溢出位。因此,在现实世界中,在汇编程序/操作码级别上,整数溢出总是定义良好的。事实上,许多汇编程序,或与硬件相关的C,在很大程度上依赖于定义良好的整数溢出(尤其是定时器硬件的驱动程序)。

在标准化之前,最初的C语言可能没有详细考虑这样的事情。但当C被ANSI和ISO标准化时,它们必须遵循某些标准化规则。ISO标准不允许偏向某项技术,从而使某家公司在竞争中占据优势。

因此,他们不得不考虑一些CPU可能会实现一些模糊的东西,比如补码、"符号和大小"或"某种实现定义的方式"。他们必须允许有符号的零、填充位和其他模糊的有符号整数机制。

正因为如此,有符号数字的行为变得非常模糊。当C中的有符号整数溢出时,您无法判断会发生什么,因为有符号整数可能用2的补码、1的补码或其他实现定义的疯狂来表示。因此,整数溢出是未定义的行为。

这个问题的合理解决方案不是发明一些安全范围检查,而是声明C语言中的所有有符号整数都应具有2的补码格式,即故事的结尾。然后,一个无符号的字符将始终为0到127,溢出到-128,所有内容都将定义良好。但人为的标准官僚作风阻止了标准的健全。

在C标准中有许多类似的问题。对齐/填充、endianes等。

因为很少需要它。您什么时候真正需要检测整数溢出?在几乎所有需要检查某个范围的情况下,通常都需要定义实际范围,因为这个范围完全取决于应用程序和算法。

您什么时候真正需要知道结果是否已溢出int的范围,而不是知道结果是否在特定算法的允许域内,或者索引是否在数组的边界内?是你为变量提供了语义,语言规范只为你提供了类型的总体范围,如果你选择了一个范围不符合你需求的类型,那就是你的错。

整数溢出是UB,因为你很少真正关心它。如果我的unsigned char在操作中溢出,我可能选择了错误的类型来累积1000万个数字。但是,在运行时了解溢出对我没有帮助,因为我的设计无论如何都被破坏了。

为什么?好吧,因为当C++开始的时候,它们还不在C中,而且从那时起,没有人提出这样的函数,也没有人成功地说服编译器制造商和委员会成员,它们足够有用,可以提供。

请注意,编译器确实提供了这类内部函数,所以它们并不反对它们。

还要注意,有一些命题可以标准化诸如不动点算术和无界精度整数类型之类的东西。

所以可能只是兴趣不够。