What happens if there is a runtime error?
First, let's see a few examples of what can go wrong.
Uninitialized local variables
void setup() {
int status;
pinMode(13, OUTPUT);
digitalWrite(13, status);
}
As pointed out by Edgar Bonet in the comments, local variables like status
in the code above are not implicitly initialized by the C++ compiler. So, the outcome of the code above is indeterminate. To avoid that, make sure you always assign values to your local variables.
Things are a bit different with global and static variables:
Global and static variables are guaranteed to be initialized to 0 by the C standard.
Source: AVR Libc Reference Manual - Frequently Asked Questions - Shouldn't I initialize all my variables?
That means you shouldn't worry about initializing them to 0 in your code. In fact, you should really avoid it, as the initialization may waste memory. Only initialize them to values other than 0.
Memory overflow
int array[10];
int v = array[100];
array[-100] = 10;
The first problem here is that you don't know what will be assigned to v, but worse is that you don't know what you messed up with the assignment to position -100 of array
.
Jump to an illegal instruction
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
}
The first call to funcPtr()
will actually be a call to doSomething()
. Calls like the second one may lead to undefined behavior.
Other bad things that may happen
Well, you can run out of RAM, for example. What else. In any case, I think your program will keep running, probably not the way you intended it to.
Kinds of Protection
In computer systems, problems like these are usually dealt with at various levels:
- By the compiler
- By the programming language runtime (as in Java for example).
- By the operating system or the processor (if your memory access a position outside the boundaries of the address space reserved to your program, the OS or the processor may have safety mechanisms to prevent that)
Arduinos only have limited protection of the compiler, and probably nothing else. The good news is that they aren't multi-tasked, so the only program being affected is yours. In any case, any of those bugs will lead to erratic behavior.
The Answers
The assumptions are the all of the problems I stated above are runtime problems.
What happens if there is a runtime error in a program?
The program will continue and what happens will depend on the side-effects of the runtime error. A call to the null function pointer will probably make the program jump to an unknown location.
Will execution of the program just stop?
No, it will keep going as if nothing extraordinary happened, probably doing what you didn't intend it to do. It may reset or act erratically. It may turn some inputs into outputs and burn a sensor or two (but that's highly unlikely).
Is there some way I get the Arduino to tell me what the error is?
I don't think so. As I said earlier, the protection mechanisms aren't there. There's no runtime support from the language, no OS, no hardware checks for out-of-bounds memory access (the bootloader doesn't count as either). You just have to be careful with your program and probably set your own safety nets.
The reason for the lack of protection is probably because Arduino controllers are too cheap, have too little memory, and should not run anything too important (yes, there seems to be a disclaimer by AVR somewhere for you not to use the MCUs normally used by Arduino in life support systems).
There are no runtime exceptions. There is only undefined behaviour.
Really, there are no exceptions at all. If you try to perform an invalid operation, it's results will be unknown.
There is no runtime checking at all, except what you implement. Your program is running on bare-metal hardware. It's the Desktop equivalent of running in ring-0 all the time, because the ATmega doesn't have rings.
There is one mechanism that can get MCU from erratic state and it's the watchdog timer. If you're implementing some code that will repeatedly run in a loop, that will not run anytime longer than some fixed time, you can set this time as watchdog period and enable the timer.
Then, you have to repeatedly reset the timer in the loop. If your code freezes at some condition loop that will never end, then the watchdog will count to zero and eventually reset the MCU.
This way you are losing data, but if you run the AVR WDT in interrupt mode, you can store some data before resetting the MCU.
So the watchdog timer can guard your code from occasional unintended endless loops.
Documentation: AVR132: Using the Enhanced Watchdog Timer