H07P-DebuggingWithVS.pdf
(
710 KB
)
Pobierz
CS106B
Winter 07-08
Handout #7P
January 14, 2008
Debugging with Visual Studio 2005
This handout was originally by Eric Roberts and Stacey Dorr. Revised by the Justins and Julie, and again by Jason and Julie.
As soon as we started programming, we found to our surprise that it
wasn’t as easy to get programs right as we had thought. We had to
discover debugging. I can remember the exact instant when I realized that
a large part of my life from then on was going to be spent in finding
mistakes in my own programs.
— Maurice Wilkes, 1949
The purpose of this handout is twofold: to give you a sense of the philosophy of debugging and
to teach you how to use some of the practical tools that make testing and debugging easier.
The philosophy of debugging
Debugging is one of the most creative and intellectually challenging aspects of programming. It
can also be one of the most frustrating. To a large extent, the problems that people face
debugging programs are not so much technical as they are psychological. To become successful
debuggers, you must learn to think in a different way. There is no cookbook approach to
debugging, although Nick Parlante’s rules below will probably help. What you need is insight,
creativity, logic, and determination.
The programming process leads you through a series of tasks and roles:
Design
—
Architect
Coding
—
Engineer
Testing
—
Vandal
Debugging
—
Detective
These roles require you to adopt distinct strategies and goals, and it is often difficult to shift your
perspective from one to another. Although debugging is extremely difficult, it can be done. It
will at times take all of the skill and creativity at your disposal, but you can succeed if you are
methodical and don’t give up on the task.
Debugging is an important skill that you will use every day if you continue in Computer Science
or any related field. Even though it is the final task of those listed above, it is certainly not the
least important. Debugging will almost always take more time than the first three tasks
combined. Therefore, you should always plan ahead and allow sufficient time for testing and
debugging, as it is required if you expect to produce quality software. In addition, you should
make a concentrated effort to develop these skills now, as they will be even more important as
programs become more complicated later in the quarter and later in your career as a programmer.
–2–
The Eleven Truths of Debugging
1. Intuition and hunches are great—you just have to test them out. When a hunch and a fact
collide, the fact wins.
2. Don’t look for complex explanations. Even the simplest omission or typo can lead to very
weird behavior. Everyone is capable producing extremely simple and obvious errors from time
to time. Look at code critically—don’t just sweep your eye over that series of simple
statements assuming that they are too simple to be wrong.
3. The clue to what is wrong in your code is in the values of your variables and the flow of
control. Try to see what the facts are pointing to. The computer is not trying to mislead you.
Work from the facts.
4. Be systematic. Be persistent. Don’t panic. The bug is not moving around in your code,
trying to trick or evade you. It is just sitting in one place, doing the wrong thing in the same
way every time.
5. If you code was working a minute ago, but now it doesn’t—what was the last thing you
changed? This incredibly reliable rule of thumb is the reason your section leader told you to
test your code as you go rather than all at once.
6. Do not change your code haphazardly trying to track down a bug. This is sort of like a
scientist who changes more than one variable at a time. It makes the observed behavior much
more difficult to interpret, and you tend to introduce new bugs.
7. If you find some buggy code which does not seem to be related to the bug you were
tracking, fix the bug code anyway. Many times the buggy code is related to or obscures the
original bug in a way you had not imagined.
8. You should be able to explain in Sherlock Holmes style the series of facts, tests, and
deductions which led you to find a bug. Alternately, if you have a bug but can’t pinpoint it,
then you should be able to give an argument to a critical third party detailing why each one of
your functions cannot contain the bug. One of these arguments will contain a flaw since one of
your functions does in fact contain a bug. Trying to construct the arguments may help you to
see the flaw.
9. Be critical of your beliefs about your code. It’s almost impossible to see a bug in a
functions when your instinct is that the function is innocent. In that case, only when the facts
have proven without question that the function is the source of the problem will you be able to
see the bug.
10. You need to be systematic, but there is still an enormous amount of room for beliefs,
hunches, guesses, etc. Use your intuition about where the bug probably is to direct the order
that you check things in your systematic search. Check the functions you suspect the most
first. Good instincts will come with experience.
11. Debugging depends on an objective and reasoned approach. It depends on overall
perspective and understanding of the workings of your code. Debugging code is more
mentally demanding than writing code. The longer you try to track down a bug without
success, the less perspective you tend to have. Realize when you have lost the perspective on
your code to debug. Take a break. Get some sleep. Many times a programmer can spend
hours late at night hunting for a bug only to finally give up at 4:00
A.M.
The next day, they find
–3–
the bug in 10 minutes. What allowed them to find the bug the next day so quickly? Maybe
they just needed some sleep and time for perspective. Or maybe their subconscious figured it
out while they were asleep. In any case, the “go do something else for a while, come back, and
find the bug immediately” scenario happens too often to be an accident.
— Nick Parlante, Stanford University
Testing incrementally
When dealing with any program containing more than a couple of functions, we highly
recommend that you test incrementally. This might include writing test code that passes values
to a function, and gets its return value. Doing this allows you to test your functions in isolation,
allowing you to hone in on bugs before putting everything together. Realizing there's a bug in a
specific 10-line function by seeing the results of a very simple test program is much easier than
testing it in the context of the rest of your program, where it is no longer isolated and much
harder to tell whether a bug is a result of that function, or any other one. This makes debugging
much less painful in the long run.
Assessing the symptoms
For programs that use random numbers, it is important to debug the program in a deterministic
environment. A deterministic environment means that a piece of code will behave the same way
every time you run it. For this reason, it is essential to take the
Randomize
statement out of the
main program, usually by enclosing it in comment markers. You want it there eventually, but
not while you’re debugging. You want your program to work the same way each time, so that
you can always get back to the same situation.
Keeping an open mind
One of the most important techniques to master about debugging is keeping an open mind. So
often, the problems that keep your code from working are easy to see if you can simply
overcome the psychological blinders that keep you from seeing them. The more you are sure
that some piece of code is correct, the harder it is to find the bugs in it.
Dealing with pointers
Programs involving pointers can be very tricky to debug, because errors in pointer handling can
lead to very strange consequences. With pointers, you can delete something twice and/or use a
variable after it has been freed, run off the end of the array, etc., and you can get away with it at
the time, but later the ill effects begin to show. This is why it is so important to have your
memory model straight before you start coding, and it doesn't hurt to make a memory diagram to
make sure you understand what you want, and compare it to what you have actually coded.
Also, be extra careful when scrutinizing allocations and initializations of pointers.
Debugging with
cout
Interspersing code with
cout
statements can be a quick and easy way to check the value of a
variable name at a certain point in a program, or to see whether or not a specific point of
execution is being reached. It is important to note that the way
cout
works may not be quite
what you would expect. Usually there is a buffer that stores the data piped to calls to
cout
. The
–4–
data in the buffer is only displayed on the screen once
endl
is sent, or a function requiring input
is called (like
GetInteger()
or
GetLine()
).
For example:
int main()
{
int i, int j;
i = ComplicatedFn();
cout << "got past ComplicatedFn(); i = " << i << ".";
j = ComplicatedFn2();
cout << "got past ComplicatedFn2(); j = " << j << ".";
ComplicatedFn3(&i, &j);
cout << "done: i = " << i << ", j = " << j << endl;
}
return 0;
// debug
// debug
// debug
Assuming no other I/O calls are in the other functions, no
cout
statements will print until the last
cout
call is executed, since it has the first (and only)
endl
call. Therefore, if your program
crashes in the middle of
ComplicatedFunction2()
, the output window will not have printed out
anything, even though a
cout
call had been passed. Therefore, it is always a good idea to attach
endl
to all debugging statements, so the buffer is flushed and the debug statements print to the
screen when they are passed.
Even though
cout
statements may seem like a sufficient means for debugging, it becomes
largely inadequate for more complicated programs, especially those which make many iterations
over the same code during a single program run. In cases where
cout-style
debugging is largely
inefficient and/or inadequate, we turn to the debugger.
Using an online debugger
Because debugging is a difficult but nonetheless critical task, it is important to learn the tricks of
the trade. The most important of these tricks is to get the computer to show you what it’s doing,
which is the key to debugging. The computer, after all, is there in front of you. You can watch it
work. You can’t ask the computer why it isn’t working, but you can have it show you its work
as it goes. Modern programming environments usually come equipped with a
debugger,
which
is a special facility for monitoring a program as it runs. By using the Visual Studio debugger, for
example, you can step through the operation of your program and watch it work. Using the
debugger helps you build up a good sense of what your program is doing, and often points the
way to the mistake.
–5–
Using the Microsoft Visual Studio debugger
Figure 1: The Visual Studio Debugger Window
The Visual Studio debugger is a complicated debugging environment, but with a little patience,
you should be able to learn it to the point where you can produce correct code more efficiently
and productively.
Under the Debug menu, there are two menu items for executing your code: the
Start
(F5) option
and the
Start without Debugging
(Ctl-F5) option. You should always use the
Start
option so
that you can set breakpoints and debug your code more easily. The debugger gives you the
ability to stop your program mid-stream, poke around and examine the values of variables, and
investigate the aftermath of a fatal error to understand what happened. If you set a breakpoint at
the first statement of your
main()
function, when you choose the
Start
command, the debugger
sets up your program and brings up the windows without starting program execution. At this
point, you control the execution of the program manually using the buttons on the debugger
window. You can choose to step through the code line-by-line, run until you get to certain
points, and so on.
Plik z chomika:
m_r_k
Inne pliki z tego folderu:
Assign3PCRecursion.zip
(227 KB)
Assign4PCBoggle.zip
(695 KB)
Assign4MacBoggle.zip
(524 KB)
Assign2PCADTs.zip
(270 KB)
Assign3MacRecursion.zip
(227 KB)
Inne foldery tego chomika:
Assign2PCADTs
Zgłoś jeśli
naruszono regulamin