【问题标题】:Difference between runtime errors and logic errors运行时错误和逻辑错误之间的区别
【发布时间】:2015-03-21 15:39:00
【问题描述】:

我已经学习 C++ 两个月了,现在我正在学习我的书(Programming Principles and Practice using C++)中关于错误的章节。但是在研究了第一页之后,我有一个问题:运行时错误和逻辑错误有什么区别?根据我的书,运行时错误是在运行时通过检查发现的错误,我们可以进一步将运行时错误分类为:

  • 硬件错误/操作系统错误
  • 库检测到的错误
  • 用户代码检测到的错误(什么是用户代码?)

而逻辑错误只是程序员在寻找错误结果的原因时发现的错误。

我以为我已经理解了这种差异,但后来作者提供的一个例子让我产生了疑问。这是一个例子:

#include "std_lib_facilities.h"

int area(int lenght, int width) // calculate area of rectangle 
{
    return lenght * width; 
}

int framed_area(int x, int y) // calculate area within frame 
{
    return area(x - 2, y - 2); 
}

int main()
{
    int x = -1;
    int y = 2; 
    int z = 4; 
    int area1 = area(x, y); 
    int area2 = framed_area(1, z); 
    int area3 = framed_area(y, z); 
    double ratio = double(area1) / area3; 
}

以下是作者对这个例子的简要说明:

函数 area() 和 framed_area() 的调用导致否定 代表区域的值,分配给 area1 和 area2。应该 我们接受这样的错误结果吗?但在回答这些问题之前 看比率的计算,在比率area3的计算中 将为 0,除以 0 将导致硬件检测错误 用一些神秘的消息终止程序。这是 您或您的用户必须处理的错误类型 不要检测并明智地处理运行时错误。

我在这里不明白的是,为什么将负值用作计算面积的函数的参数被视为运行时错误,这不只是逻辑错误吗?我认为运行时错误只是由于例如将数字除以 0 和其他特殊情况而导致的错误。我错了还是我只是误解了什么? 逻辑错误和运行时错误之间的真正区别是什么?你能举几个小例子吗?

【问题讨论】:

    标签: c++ runtime-error


    【解决方案1】:

    运行时错误可能会合法发生:例如一些包含垃圾数据的文件,或者一些错误的人工输入,或者一些资源不足(没有更多的内存、磁盘空间已满、硬件损坏、网络连接失败)。

    逻辑错误(或失败的assert ....)根据定义总是是程序中某些错误的症状,例如用于二分访问的假定排序数组,恰好未排序。

    查看documentation of <stdexcept> 标头:

    std::logic_error:该类定义抛出的对象类型为异常,以报告程序内部逻辑中的错误,例如违反逻辑前提条件或类不变量。

    std::runtime_error:该类定义了抛出的对象类型作为异常报告只能在运行时检测到的错误。

    我认为 std::logic_errorinternal logical 是一个错字,我将其理解为 internal logic(程序的),但我不是母语为英语的人。

    如果您将程序规范形式化(例如,在 Frama C 的帮助下使用 ACSL),您可能会发现(并且可能是正确的)逻辑错误;但你应该关心运行时错误。但是您也可能在规范中存在错误。

    了解Ariane 5 flight 501 failure。并查看J.Pitrat's blog 以获得其他意见。

    【讨论】:

      【解决方案2】:

      您的书在第一个细分方面当然是正确的,即编译时与运行时。
      如果可以,请确保使用前者而不是后者,因为它会自动检测到。

      两者都可以进一步细分也没错,但我们只看后者:

      • 外部错误:硬件错误/操作系统错误

        其中许多是意料之中的,例如网络不可靠、用户文件丢失等。通常,人们可以从中恢复过来。

        其他一些是意料之外的,并且通常是不可恢复的,例如缺少所需的依赖项、不可靠的 RAM、时钟耗尽等。期待简单的崩溃和燃烧。

      • 输入错误:输入数据错误、输入数据丢失等等。

        这应该由您的应用程序检测到,这取决于错误的严重程度可能会替代默认值,尝试恢复它或只是崩溃和烧毁。

      • 逻辑错误:发现程序的基本假设不正确。

        没有什么可以再依赖了,唯一明智的选择是立即崩溃并燃烧以试图控制损坏。

        常见的逻辑错误是 fencepost-errors (off-by-one)、race-conditions、use-after-free 等。

      请注意,std::logic_error,尽管它的名字,并不总是意味着程序逻辑中的致命故障,因此可能是预期的并且相对良性。

      【讨论】:

        【解决方案3】:

        关于您提供的示例,向方法计算区域提供负输入是一个逻辑错误,因为您(作为应用程序的开发人员)已经知道这是错误的。但是假设您编写了计算面积的方法,并将其提供给不知道计算面积是什么的用户 - 他可能提供不正确的输入,这将导致不正确的结果。在这种情况下,这将是一个运行时错误,因为用户没有意识到并且没有适当的错误检查。

        【讨论】:

        • 总之,如果我不处理特定类型的错误,它是运行时错误吗?我仍然不明白为什么,因为按照我的书所说,我已经理解逻辑错误只是一些代码没有做它应该做的事情,那么为什么你提供给我的例子是运行时错误?如果我提供一个负数作为计算面积的函数的参数,这不是一个简单的逻辑错误吗?你能用更简单的方式向我解释一下吗?对不起,我只是一个初学者
        【解决方案4】:

        逻辑错误是由于程序员的思维缺陷造成的,是可以预防的。例如,忘记防范数组中的越界错误。

        另一方面,运行时错误是由计算机体系结构和操作系统引起的,超出程序员的控制范围,在编码过程中无法轻易预测。

        【讨论】:

          【解决方案5】:
          #include <iostream>
          
          using std::cout;
          using namespace std;
          
          int area(int lenght, int width) // calculate area of rectangle 
          {
          return lenght * width; 
          
          }
          
          
          int framed_area(int x, int y) // calculate area within frame 
          {
          
          return area(x - 2, y - 2); 
          
          }
          
          int main()
          {
          
          
          int x = -1;
          int y = 2; 
          int z = 4; 
          
          
          int area1 = area(x, y); 
          int area2 = framed_area(1, z); 
          int area3 = framed_area(y, z); 
          double ratio = double(area1) / area3; 
          
          cout<<area1<<endl
          <<area2<<endl
          <<area3<<endl
          <<ratio<<endl;
          
          system("pause");
          return 0;
          }
          

          试试这个没有运行时错误怎么样,不知道没有cout怎么能看到输出结果。

          我也是个新手,我觉得学习的时候短时间练习很重要。

          这只是我的建议,因为上面的两个答案已经很好地解释了。

          【讨论】:

          • @golnDoor 如果 y 为 0,则 x/y 的结果未定义,如果 x 或 y 为负数,则 x % y 的结果是实现定义的
          • @golnDoor 计算对除以 0 的反应是不同的(这就是为什么它被称为“未定义行为”),例如,如果我运行该程序,我不会收到运行时错误,但是一个“奇怪”的值。喜欢 1.23#2ecc。
          【解决方案6】:

          根据我自己的经验:) ...

          当我们应该为自己构建一个标准异常逻辑时,这种混淆可能源于我们通过重用标准异常逻辑来“让生活变得简单”。 std 异常将被我们的代码捕获不抛出)。它们告诉我们从 std 库调用函数时发生了什么,并且异常继承树的逻辑不能简单地转移到库之外。

          std::runtime_error 告诉我们 嗯!发生了一些不可预测的事情

          std::logic_error 告诉我们哦哦!您在代码中遗漏了一些重要的内容

          你的老板可以写这样的程序

          try { mainCall(); } 
          catch (const std::logic_error &) { std::cout << "No premiums this year!"; }
          catch(const std::runtime_error &) { std::cout << "I know it’s not your fault..."; }
          

          现在,当我们想在代码中重用这个逻辑时,问题就出现了。 std::string 构造函数和 .what() 方法都有例外,可以使用,那么为什么要重新发明轮子,对吧?

          现在困境来了:

          double a,b;
          std::cin >> a;
          std::cin >> b;
          if (b == 0) EXCEPTION;
          else std::cout << a/b;
          

          那么EXCEPTION 是什么样的?是runtime_error 还是logic_error

          • 发生了不可预知的事情吗?我们能期望用户为 b 输入 0 吗?是的,我们可以而且我们确实做到了!所以一定不是runtime_error
          • 我们是否在代码中遗漏了一些重要的内容?我们没有!所以也不能是logic_error

          我们直觉上倾向于将runtime_errors 分配给硬件错误,将logic_errors 分配给“逻辑”错误——因此在上面的示例中人们会选择logic_error。但是直觉通常是编程的一个坏指南......只需将你的代码提供给你老板的测试单元;)

          结论:

          从 std::exception(或其他)开始导出您自己的异常树,或者不要尝试解决 runtime/logic_error 困境。 它在 std 库域之外没有解决方案……

          【讨论】:

            【解决方案7】:

            当您的计算机系统(操作系统和/或固件)必须处理意外指令时,就会发生运行时错误。大多数情况下,当发生运行时错误时,您的程序就会崩溃。例如,内存泄漏是分配了从未释放的新内存。但是,当您的程序中有不可预测的行为时,可以很容易地识别出逻辑错误。假设您希望您的程序将两个数字相加,但它却将它们相乘。这是一种逻辑错误,只需使用正确的运算符即可修复。

            希望这会有所帮助

            【讨论】:

            • 我不认为内存泄漏是运行时错误的一个很好的例子,因为它通常不会产生任何错误消息 - 直到你用完内存(这可能永远不会发生) .除以零(如问题)可能是一个更好的例子。此外,逻辑错误通常根本无法“轻松”识别 - 虽然运行时错误通常会终止程序,但逻辑错误可能允许“意外行为”继续未被检测到。但是,是的,当发生运行时错误时,程序通常会崩溃,而硬件/操作系统(通常)不会检测到逻辑错误。
            猜你喜欢
            • 1970-01-01
            • 2013-06-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-02
            • 2017-09-13
            相关资源
            最近更新 更多