• Tidak ada hasil yang ditemukan

Errors!

4.7 Avoiding and finding errors

4.7.2 Debugging

When you have written (drafted?) a program, it’ll have errors. Small programs do occasionally compile and run correctly the first time you try.

But if that happens for anything but a completely trivial program, you should at first be very, very suspicious. If it really did run correctly the first time, go tell your friends and celebrate – because this won’t happen every year.

So, when you have written some code, you have to find and remove the errors. That process is usually called debugging and the errors bugs. The term bug is often claimed to have originated from a hardware failure caused by insects in the electronics in the days when computers were racks of vacuum

tubes and relays filling rooms. Several people have been credited with the discovery and the application of the word bug to errors in software. The most famous of those is Grace Murray Hopper, the inventor of the COBOL

programming language (PPP2.§22.2.2.2). Whoever invented the term more than 50 years ago, bug is evocative and ubiquitous. The activity of

deliberately searching for errors and removing them is called debugging.

Debugging works roughly like this:

[1] Get the program to compile.

[2] Get the program to link.

[3] Get the program to do what it is supposed to do.

Basically, we go through this sequence again and again: hundreds of times, thousands of times, again and again for years for really large programs. Each time something doesn’t work we have to find what caused the problem and fix it. I consider debugging the most tedious and time-wasting aspect of programming and will go to great lengths during design and programming to minimize the amount of time spent hunting for bugs. Others find that hunt thrilling and the essence of programming – it can be as addictive as any video game and keep a programmer glued to the computer for days and nights (I can vouch for that from personal experience also).

XX

Here is how not to debug:

Click here to view code image

while (the program doesn't appear to work){ // pseudo code

Randomly look through the program for something that "looks odd"

Change it to look better }

AA

Why do we bother to mention this? It’s obviously a poor algorithm with little guarantee of success. Unfortunately, that description is only a slight

caricature of what many people find themselves doing late at night when feeling particularly lost and clueless, having tried “everything else.” The key question in debugging is

How would I know if the program actually worked correctly?

If you can’t answer that question, you are in for a long and tedious debug session, and most likely your users are in for some frustration. We keep returning to this point because anything that helps answer that question minimizes debugging and helps produce correct and maintainable programs.

Basically, we’d like to design our programs so that bugs have nowhere to hide. That’s typically too much to ask for, but we aim to structure programs to minimize the chance of error and maximize the chance of finding the errors that do creep in.

4.7.2.1 Practical debug advice AA

Start thinking about debugging before you write the first line of code. Once you have a lot of code written it’s too late to try to simplify debugging.

Decide how to report errors: “Use error() and catch exception in main()” (§4.6) will be your default answer in this book.

AA

Make the program easy to read so that you have a chance of spotting the bugs:

Comment your code well. That doesn’t simply mean “Add a lot of

comments.” You don’t say in English what is better said in code. Rather, you say in the comments – as clearly and briefly as you can – what can’t be said clearly in code:

The name of the program The purpose of the program Who wrote this code and when Version numbers

What complicated code fragments are supposed to do What the general design ideas are

How the source code is organized

What assumptions are made about inputs

What parts of the code are still missing and what cases are still not handled

References to supporting material (e.g., to a book explaining the algorithms used)

Use meaningful names.

That doesn’t simply mean “Use long names.”

Use a consistent layout of code.

Your IDE tries to help, but it can’t do everything, and you are the one responsible.

The style used in this book is a reasonable starting point.

Break code into small functions, each expressing a logical action.

Try to avoid functions longer than a page or two; most functions will be much shorter.

Avoid complicated code sequences.

Try to avoid nested loops, nested if-statements, complicated conditions, etc. Unfortunately, you sometimes need those, but remember that complicated code is where bugs can most easily hide.

Use library facilities rather than your own code when you can.

A library is likely to be better thought out and better tested than what you could produce as an alternative while busily solving your main problem.

This is pretty abstract just now, but we’ll show you example after example as we go along.

AA

Get the program to compile. Obviously, your compiler is your best help here.

Its error messages are usually helpful – even if we always wish for better ones – and, unless you are a real expert, assume that the compiler is always right; if you are a real expert, this book wasn’t written for you. Occasionally, you will feel that the rules the compiler enforces are stupid and unnecessary (they rarely are) and that things could and ought to be simpler (indeed, but they are not). However, as they say, “a poor craftsman curses his tools.” A good craftsman knows the strengths and weaknesses of his tools and adjusts his work accordingly. See §4.3 for common compile-time errors and §4.4 for some link-time errors.

After the program compiles and links, next comes what is typically the hardest part: figuring out why the program doesn’t do what it’s supposed to.

You look at the output and try to figure out how your code could have produced that. Actually, first you often look at a blank screen (or window), wondering how your program could have failed to produce any output.

When looking for a bug, carefully follow the code statement by statement from the last point that you are sure it was correct. Pretend you’re the

computer executing the program. Does the output match your expectations?

Of course not, or you wouldn’t be debugging.

AA

Often, when we don’t see the problem, the reason is that we “see” what we expect to see rather than what was written. Consider:

Click here to view code image

for (int i = 0; 0<=max; ++i) { // oops!

for (int j = 0; j<v.size(); ++i); // print the elements of v cout << "v[" << j << "]==" << v[j] << '\n';

// ...

}

This last example is equivalent to code from a real program written by a experienced programmer. We expect it was written very late some night.

Often when we do not see the problem, the reason is that there is too much code being executed between the point where the program produced the last good output and the next output (or lack of output). Most programming environments provide a way to execute (“step through”) the statements of a program one by one. Eventually, you’ll learn to use such facilities, but for

simple problems and simple programs, you can just temporarily put in a few extra output statements (using the error-reporting output stream cerr) to help you see what’s going on. For example:

Click here to view code image

int my_fct(int a, double d) {

cerr << "my_fct(" << a << "," << d << ")\n";

int res = 0;

// ... misbehaving code here ...

cerr << "my_fct() returns " << res << '\n';

return res;

}

Insert statements that check invariants (that is, conditions that should always hold; see §4.7.3 and §8.4) in sections of code suspected of harboring bugs.

For example:

Click here to view code image

int my_complicated_function(int a, int b, int c) // the arguments are positive and a < b < c {

if (!(0<a && a<b && b<c)) // ! means "not" and && means "and"

error("bad arguments for mcf");

// ...

}

If that doesn’t have any effect, insert invariant checks in sections of code not suspected of harboring bugs; if you can’t find a bug, you are almost certainly looking in the wrong place.

Dalam dokumen ePUB eBook Customization Guide (Halaman 169-174)