如果出现运行时错误怎么办?


17

如果程序中出现运行时错误,该怎么办?程序的执行会停止吗?有什么办法可以让Arduino告诉我错误是什么吗?

Answers:


21

首先,让我们看一些可能出问题的示例。

未初始化的局部变量

void setup() {
  int status;
  pinMode(13, OUTPUT);
  digitalWrite(13, status);
} 

正如Edgar Bonet在评论中所指出的那样,statusC ++编译器并未隐式初始化像上面代码中所示的局部变量。因此,以上代码的结果是不确定的。为避免这种情况,请确保始终将值分配给局部变量。

全局变量和静态变量有些不同:

保证全局和静态变量都可以通过C标准初始化为0。

来源: AVR Libc参考手册-常见问题-我不应该初始化所有变量吗?

这意味着您不必担心在代码中将它们初始化为0。实际上,您应该避免使用它,因为初始化可能会浪费内存。仅将它们初始化为非0的值。

内存溢出

int array[10];
int v = array[100];
array[-100] = 10;

这里的第一个问题是,您不知道将为v分配什么,但更糟糕的是,您不知道对分配给-100的位置搞砸了什么array

跳转到非法指令

void doSomething( void ) { 
    for (int i = 0; i < 1000; i++); 
}

void setup () 
{
    void (*funcPtr)( void );

    funcPtr = &doSomething;
    funcPtr(); // calls doSomething();

    funcPtr = NULL;
    funcPtr(); // undefined behavior
}

的第一个呼叫funcPtr()实际上将是的呼叫doSomething()。像第二个这样的调用可能导致不确定的行为。

其他可能发生的坏事

好吧,例如,您可能会用完RAM。还有什么。无论如何,我认为您的程序将继续运行,可能不是您希望的那样。

保护种类

在计算机系统中,通常在各个级别处理此类问题:

  1. 由编译器
  2. 通过编程语言运行时(例如在Java中)。
  3. 通过操作系统或处理器(如果内存访问的位置超出了为程序保留的地址空间的边界,则操作系统或处理器可能具有安全机制来防止这种情况)

Arduino仅对编译器提供有限的保护,可能没有其他保护。好消息是它们不是多任务的,所以唯一受影响的程序就是您的程序。无论如何,这些错误中的任何一个都将导致错误的行为。

答案

这些假设是我上面提到的所有问题,都是运行时问题。

如果程序中出现运行时错误,该怎么办?

该程序将继续,发生的情况将取决于运行时错误的副作用。调用null函数指针可能会使程序跳转到未知位置。

程序的执行会停止吗?

不,它会继续前进,好像没有什么特别的事情发生,可能是您未曾打算做的。它可能会重置或行为异常。它可能会将某些输入变成输出,并烧掉一两个传感器(但这极不可能)。

有什么办法可以让Arduino告诉我错误是什么?

我不这么认为。正如我之前所说,保护机制并不存在。语言没有运行时支持,没有操作系统,也没有硬件检查越界内存访问(引导加载程序也不算在内)。您只需要对程序小心,并可以设置自己的安全网。

缺乏保护的原因可能是因为Arduino控制器价格太便宜,内存太少并且不应运行太重要的东西(是的,AVR似乎在某处声明您不使用通常使用的MCU。生命支持系统中的Arduino)。


1
大!到目前为止,我在Arduino.SE上看到的最好的答案!
帽子的家伙

1
谢谢!!我认为我们应该努力给出尽可能好的答案。但这让我有些担心,因为我们没有那么多的REAL EE专家可以查看像我这样的答案并发现任何明显的错误。实际上,这就是我发布答案的原因,尽管我对AVR MCU不太了解。就是看看我们是否有人来纠正它。我们肯定不希望像我这样的聪明人说出不正确的话并摆脱它。但这可能是对Meta网站的讨论。
里卡多

5
@Ricardo-我要说的一句话是,未明确初始化的变量不一定未初始化。在函数外部定义的变量通常具有“自动存储持续时间”,然后将其默认初始化为零。有关更多信息,请参见en.cppreference.com/w/cpp/language/default_initialization。初始化行为非常复杂,以至于依赖它可能很危险,但是进行总体声明可能不是一个好主意。
康纳·沃尔夫

1
此外,SRAM在复位或启动时被初始化为0,因此,如果您想生存下去,可以对未初始化的变量进行一些明智的猜测。您不应该依赖这种行为,但这很有趣。
康纳·沃尔夫

1
有一个有趣的示例,说明当您用尽SRAM时会发生什么:electronics.stackexchange.com/questions/42049/…。基本上,堆栈会破坏堆的一部分,反之亦然。这可以做一些有趣的事情,例如破坏堆栈框架的某些部分(破坏函数返回等),或将无效数据写入变量。
康纳·沃尔夫

9

没有运行时例外。只有未定义的行为。

真的,没有例外可言。如果您尝试执行无效的操作,其结果将是未知的。

除了实现的内容外,根本没有运行时检查。您的程序在裸机硬件上运行。这与台式机始终在ring-0中运行是等效的,因为ATmega 并没有ring


6

有一种机制可以使MCU摆脱不稳定状态,它是看门狗定时器。如果您要实现一些将在循环中重复运行的代码,而这些代码的运行时间不会超过某个固定时间,则可以将此时间设置为看门狗周期并启用计时器。

然后,您必须在循环中重复重置计时器。如果您的代码冻结在永远不会结束的某些条件循环中,则看门狗将计数为零,并最终复位MCU。

这样,您将丢失数据,但是如果以中断模式运行AVR WDT,则可以在复位MCU之前存储一些数据。

因此,看门狗定时器可以保护您的代码免于意外的无尽循环。

文档:AVR132:使用增强型看门狗定时器


5

您需要一个类似这样的硬件调试器。但是通常您会看到程序没有按预期的方式运行,因此必须查看代码的该部分以识别问题。

一种常见/快速/简便的方法是添加打印语句以打印出变量或任何值,这样您就可以知道程序到了代码中的正确位置。这将帮助您进一步隔离问题。

我相信VisualMicro内置了一些调试功能。


3

我假设AVR CPU没有任何错误检测或恢复工具。它可能只是停止,或者继续忽略错误和后果。就像sachleen所说的那样,您应该在程序中添加一些调试语句,以在操作中间打印出数据,以测试其是否正常工作。如果使用仿真器并设置断点,则很容易发现问题。


-2

Arduino将重新启动(即它将重新启动setup()loop())。


1
不必要。运行时错误可能使程序进入循环而没有重新启动。
尼克·加蒙
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.