代码之家  ›  专栏  ›  技术社区  ›  Jon Schneider Stefan

为什么默认情况下语言不会在整数溢出时引发错误?

  •  51
  • Jon Schneider Stefan  · 技术社区  · 16 年前

    integer overflow 在运行时发生而不引起任何类型的错误条件。

    例如,考虑这个(设计的)C*方法,它不考虑溢出/下溢的可能性。(为简洁起见,该方法也不处理指定列表为空引用的情况。)

    //Returns the sum of the values in the specified list.
    private static int sumList(List<int> list)
    {
        int sum = 0;
        foreach (int listItem in list)
        {
            sum += listItem;
        }
        return sum;
    }
    

    List<int> list = new List<int>();
    list.Add(2000000000);
    list.Add(2000000000);
    int sum = sumList(list);
    

    系统将发生溢出 sumList() 方法(因为 int 输入C#是32位有符号整数,列表中的值之和超过最大32位有符号整数的值)。sum变量的值为-294967296(不是4000000000);这很可能不是sumList方法的(假设的)开发人员想要的。

    显然,开发人员可以使用各种技术来避免整数溢出的可能性,例如使用类似Java的类型 BigInteger ,或 checked 关键词与 /checked C#中的编译器开关。

    does have this .)

    9 回复  |  直到 9 年前
        1
  •  40
  •   Jay Bazuzi Buck Hodges    16 年前

    在C#中,这是一个性能问题。具体来说,就是现成的基准测试。

    当C是新的时候,微软希望很多C++开发者会转向它。他们知道许多C++用户认为C++是快速的,特别是比那些在自动内存管理等方面“浪费”时间的语言更快。

    潜在的采用者和杂志评论员都可能得到一份新C#的拷贝,安装它,构建一个在现实世界中没有人会编写的简单应用程序,在一个紧密的循环中运行它,并测量它需要多长时间。然后他们会为他们的公司做出决定,或者根据这个结果发表一篇文章。

    他们的测试表明C语言比本地编译C++慢,这是一种能使人们快速摆脱C语言的东西。你的C#应用程序将自动捕获溢出/下溢的事实是他们可能会错过的。因此,默认情况下它是关闭的。

    我认为很明显,99%的时间我们都想/检查。这是一个不幸的妥协。

        2
  •  26
  •   David Hill    16 年前

    我认为表现是一个很好的理由。如果你考虑一个典型程序中的每一个增加整数的指令,如果不是简单的OP添加1,它必须检查每次添加1会溢出类型,那么额外循环的代价会相当严重。

        3
  •  16
  •   phuclv    9 年前

    您的工作假设是整数溢出始终是不希望出现的行为。

    有时需要整数溢出。我看到的一个例子是将绝对航向值表示为固定点数。给定无符号整数,0表示0或360度,最大32位无符号整数(0xFFFFFF)是360度以下的最大值。

    int main()
    {
        uint32_t shipsHeadingInDegrees= 0;
    
        // Rotate by a bunch of degrees
        shipsHeadingInDegrees += 0x80000000; // 180 degrees
        shipsHeadingInDegrees += 0x80000000; // another 180 degrees, overflows 
        shipsHeadingInDegrees += 0x80000000; // another 180 degrees
    
        // Ships heading now will be 180 degrees
        cout << "Ships Heading Is" << (double(shipsHeadingInDegrees) / double(0xffffffff)) * 360.0 << std::endl;
    
    }
    

    可能还有其他可以接受溢出的情况,类似于本例。

        4
  •  8
  •   Steve Jessop    13 年前

    C/C++从不强制执行陷阱行为。即使明显的除法0是C++中的未定义行为,而不是特定类型的陷阱。

    C语言没有任何陷阱的概念,除非你计算信号。

    C++的设计原则是,除非你要求,否则它不会引入C中没有的开销。所以Stroustrup不希望强制要求整数以需要任何显式检查的方式运行。

    一些早期的编译器和针对受限硬件的轻量级实现根本不支持异常,并且异常通常可以通过编译器选项禁用。为语言内置命令异常将是一个问题。

    即使C++已经检查了整数,早期的程序员99%也会因为性能提升而关闭。

        5
  •  7
  •   Dima    16 年前

    因为检查溢出需要时间。通常转换为单个汇编指令的每个基本数学运算都必须包括溢出检查,从而导致多个汇编指令,可能导致程序速度慢几倍。

        6
  •  6
  •   Rob Walker    16 年前

    其他1%将涵盖那些人们正在进行奇特的位操作或在混合有符号和无符号操作时“不精确”,并且想要溢出语义的情况。

        7
  •  4
  •   Salman A    16 年前

    向后兼容性是一个大问题。在C语言中,假设您对数据类型的大小给予了足够的关注,如果发生上溢/下溢,这就是您想要的。然后用C++、C和java,很少改变“内置”数据类型的工作方式。

        8
  •  0
  •   supercat    5 年前

    如果整数溢出定义为立即引发信号、引发异常或以其他方式使程序执行偏离方向,则可能溢出的任何计算都需要按指定的顺序执行。即使在整数溢出检查不会直接花费任何成本的平台上,整数溢出被截留在程序执行序列的正确位置的要求也会严重阻碍许多有用的优化。

    如果一种语言指定整数溢出将改为设置锁存错误标志,则将限制函数中对该标志的操作如何影响其在调用代码中的值,并规定在溢出不会导致错误输出或行为的情况下无需设置该标志,然后,编译器可以生成比任何类型的手动溢出检查程序员所能使用的效率更高的代码。举个简单的例子,如果C中有一个函数,它将两个数字相乘并返回一个结果,在溢出的情况下设置一个错误标志,那么无论调用方是否使用结果,编译器都需要执行乘法。然而,在我所描述的规则比较宽松的语言中,如果编译器确定没有任何东西使用乘法的结果,则可以推断溢出不会影响程序的输出,并完全跳过乘法。

        9
  •  -4
  •   devinmoore    16 年前

    我对运行时默认情况下不会出现错误的理解可以归结为希望创建具有类似ACID行为的编程语言的遗留问题。具体地说,这是一个信条,即任何你编码它去做(或不编码)的事情,它都会做(或不做)。如果您没有编写一些错误处理程序,那么机器将“假定”由于没有错误处理程序,您真的想做您告诉它要做的可笑的、容易崩溃的事情。

    http://en.wikipedia.org/wiki/ACID )

    推荐文章