As a general matter, three kinds of errors, or bugs, can occur in a program: syntax errors, runtime errors, and logical errors. We want to understand the kind of bug we have so we can squash that bug!
Syntax errors, which are the most easily dealt with kind of bug, are caused by improperly constructed code. Put a little more formally, a syntax error occurs when a statement in a program fails to meet the requirements of the language definition. Therefore, the program can’t be compiled or interpreted—meaning “made ready” for the machine to execute—by the program designed to convert your “high-level” statements to machinereadable code.
If your code contains a syntax error, it’s likely that you’ll get a syntax error message when you attempt to open it in a browser. I’ll show you how this works later in this chapter in the section “ Finding Bugs.”
The point here is that usually you can’t even get a program to run if it contains syntax errors. This makes things easier because you know you have a problem. As the saying goes, the first step to fixing a problem is knowing you have one.
Runtime errors occur when a program that has no syntax errors attempts to perform an illegal operation.
For example, dividing by zero is generally inadvertent. One scenario that causes a division by zero is if the user inputs a value of zero, which is used in a division. (Of course, it’s far more likely that the zero divisor will be the inadvertent result of a computation.)
Listing 10.1: Dividing by Zero
<HTML> <HEAD> <TITLE>Don't bug me, please! </TITLE> </HEAD> <BODY> <H1> <SCRIPT> var theNum = new Number(42 / 0); var theStr = theNum.toString(); document.write(theStr); </SCRIPT> </H1> </BODY> </HTML>
Trust me, however, when I say that in most environments attempting to divide by zero at runtime will cause an error.
Many runtime errors are caused by attempting to access resources that aren’t available for one reason or another: Because the resource doesn’t exist, the program doesn’t have proper permissions to access it, the resource hasn’t been freed, or the resource is full.
To provide examples of these kinds of errors a little more concretely— they occur when you attempt to write to a full disk, write to a file that doesn’t exist or that’s being used by another program, use a network resource that isn’t available, or log on to a database with incorrect credentials.
There’s nothing syntactically illicit about any of these things; they just can’t be done. They also can’t all be easily anticipated.
The most embarrassing thing about runtime errors is that they often don’t appear when you’re testing a program. Runtime errors generally start to make themselves known only after your program is installed and someone starts using it, but good testing methodology, as I explain later in this chapter, helps obviate this possibility.
The best thing to do is to anticipate possible runtime problems and handle them using exception handling code as explained later in this chapter. If the user might enter zero causing a divide by zero flaw, handle that possibility using a catch clause. If your program depends upon access to a network drive, handle the possibility that the drive isn’t available. And so on.
In fact, logical errors can cause inconsistent results that are sometimes correct and sometimes wrong. This situation can be particularly frustrating to debug. The subtler the problem, the harder it may be to fix (or to be sure that an error even exists).
As I’ve mentioned a number of times, one of the most common logical errors involves being “one off” when iterating through a loop. One-off errors are very common and quite pesky. You should always be on the lookout for one-off errors. Essentially, this error involves getting the termination condition for the loop wrong—it’s off by one.
Another condition that can be the source of problems is getting the termination condition of a while statement wrong.