运行时错误和逻辑错误之间的区别

Difference between runtime errors and logic errors

本文关键字:之间 区别 错误 运行时错误      更新时间:2023-10-16

我已经研究C++两个月了,现在我正在研究我的书(使用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() 的调用导致负数 值,表示区域,分配给区域 1 和区域 2。应该 我们接受这种错误的结果?但在回答这些问题之前 看比率的计算,在比率面积的计算中3 将为 0,除以 0 将导致硬件检测到错误 这会终止程序并显示一些神秘的消息。这是 您或您的用户必须处理的错误类型,如果您 不要检测并明智地处理运行时错误。

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

运行时

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

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

请参阅<stdexcept>标头的文档:

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

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

我相信std::logic_error的内部逻辑是一个错字,我将其理解为(程序的内部逻辑),但我不是母语为英语的人。

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

阅读有关阿丽亚娜5号航班501故障的信息。并查看J.Pitrat的博客以获得其他观点。

你的书在第一个细分中肯定是正确的,编译时与运行时。
如果可以,请确保获取前者而不是后者,因为它会自动检测到。

两者都可以进一步细分也是对的,但让我们只看后者:

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

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

    其他是意外的,通常无法恢复,例如缺少所需的依赖项,不可靠的RAM,耗尽时钟等。期望简单地崩溃和燃烧。

  • 输入
  • 错误:输入数据损坏,输入数据丢失,等等。

    这应该由您的应用程序检测到,根据错误的严重性,该应用程序可能会替换默认值,尝试恢复它或只是崩溃并刻录。

  • 逻辑错误:程序的基本假设被发现缺乏。

    没有什么可以再依靠了,唯一理智的选择是立即崩溃和燃烧以试图控制损害。

    常见的逻辑错误有栅栏错误(逐个关闭)、竞争条件、释放后使用等。

请注意,std::logic_error尽管名称,但并不总是意味着程序逻辑的致命崩溃,因此可能是预期的并且相对良性。

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

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

另一方面,运行时错误来自计算机体系结构和操作系统,超出了程序员的控制范围,并且在编码过程中不容易预测。

#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怎么能看到输出结果。

我也是一个新的瘦子,我认为在学习时短时间内练习很重要。

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

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

希望这有帮助

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

这种困惑可能源于我们"让生活变得简单",当我们应该为自己建立一个标准异常逻辑时,我们重复使用性病异常逻辑。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

  • 是不是发生了一些不可预测的事情?我们能预料到用户输入 0 表示 b 吗?是的,我们可以,而且我们确实做到了!所以一定不是runtime_error.
  • 我们在代码中是否遗漏了一些重要的东西?不,我们没有!所以它也不能是logic_error

我们直觉地倾向于将runtime_errors分配给硬件错误,logic_errors分配给"逻辑"错误——因此人们会在上面的例子中选择logic_error。但是直觉往往是编程中的糟糕指南......只需将代码提供给老板的测试单元;)

结论:

从 std::exception(或其他什么)开始派生你自己的异常树,或者不要尝试解决运行时/logic_error困境。它没有标准库域之外的解决方案......