煮酒论坛

 找回密码
 申请新用户
搜索
热搜: 活动 交友 discuz
查看: 4112|回复: 0

Visual Studio 6.0 (C++) Debugger Tutorial

[复制链接]
发表于 2008-9-1 15:25:08 | 显示全部楼层 |阅读模式
转自:http://www.cs.uvm.edu/~upe/resources/debugging/visualCDebug/

Contents
[size=+1]This Tutorial
This tutorial discusses how Visual Studio 6.0's debugging tools can be used to find and fix problems in C++ programs. It is designed for people who have never used a debugger before, although it may be useful to anyone who's unfamiliar with Visual Studio's debugging tools.

[size=+1]Introduction
This tutorial examines use of the Microsoft Visual Studio debugger. The debugger is a tool to help correct runtime errors -- note that no debugging tools are useful in solving compiler errors.

Compiler errors are those that show at the bottom of the screen when compiling. Things like "V:\debugger\temp.cpp(11) : error C2146: syntax error : missing ';' before identifier 'cin'" Since these are always associated with a particular line of the program, they're generally fairly easy to solve.

The dificult problems are the ones that happen while the program is running. The syntax is correct (or it wouldn't have compiled), so the error is that the code doesn't do what you think it does. When the error isn't obvious ("Oh, I never called the function that does the calculation!") it's time to start debugging.

[size=+1]Basic Debugging Technique
If the program isn't working right, one of two things could be going wrong:
1) Data is corrupt somewhere. Was something added that should be subtracted? Is some user input not being stored properly? Things like that.
2) The code doesn't execute in the right sequence. Are all the loop conditions set right? Are the functions called in the right order? Is there an if where there shouldn't be? Et cetera.

If something's broken, it has to "break" at one particular point in the program. The idea of debugging is to determine what line is causing the problem so the error can be more easily identified. It's important to note that the line on which a problem evidences itself is not necessarily where it originated. For example:
a = 0;
b = 1;
cout << (b / a) << endl;

The error actually happens on the 'cout' line, but the problem is caused two lines up where a is set to 0. In a complex program, the difference may be hundreds of lines.

The most basic way to hunt down errors (and the only way in some languages) is to echo information out to the screen: cout << "I'm in function1(), thisNumber = " << thisNumber << endl;

That works but could take a long time to sift through a thousand lines of code to find the one line causing the problem. That's where the debugger is used.

[size=+1]A Buggy Program
Trying to debug a program that's working perfectly is rather foolish, so we'll begin with a program that has some obvious problems:



Note that this program compiled cleanly (0 warnings, 0 errors), but obviously some things aren't working right. One and three AREN'T equal, and 0.85 is NOT 0%. So we'll examine the code and determine where the problems are.

The Buggy Code
Copy the following code into Visual Studio to follow the tutorial:

#include <iostream.h>#include <stdlib.h>int ToPercent (float decimal);void main() {        int a, b;        float c;        int cAsPercent;        cout << "Enter two Integers >";        cin >> a >> b;        if (a = b) cout << "They are Equal!\n";        else if (a > b) cout << "The first one is bigger!\n";        else cout << "The second one is bigger!\n";        cout << "Enter a Decimal to be Converted to Percent >";        cin >> c;        cAsPercent = ToPercent(c);        cout << "That's " << cAsPercent << "%\n";        cout << endl << endl;        system("pause");}/* ToPercent():Converts a given float (eg 0.9) to a percentage (90).*/int ToPercent (float decimal) {        int result;        result = int(decimal) * 100;        return result;}[size=+1]Debug Mode or Not?
None of the debugging tools available are useful if the program isn't being run in debug mode. Rather than just clicking the Exclamation Point icon or pressing Ctrl+F5 to run your program, the debugger must be run from the "Build" menu by clicking "Start Debug" then "Go." The F5 key alone will also run in debug mode.

Visual Studio also makes a distinction between "Build for Debug" and "Build for Release." The default is "Debug," so unless you've changed it you don't need to look for the option.

[size=+1]The Debug Toolbar
All the debugging functions can be accessed from Visual Studio's menus, but the Debug Toolbar is usually easier to use. There are also keyboard shortcuts for each function; hover your mouse over one of the toolbar's buttons (either here or in Visual Studio) to see its description and keyboard shortcut.

If the toolbar isn't showing, right-click near an existing toolbar (at the top of the screen) and select "Debug" from the list [Graphic at Left]. The toolbar can be "Docked" and "Undocked" -- drag it wherever is most convenient for you.




[size=+1]Breakpoints
Breakpoints are the lifeblood of debugging. Regardless of whether your error is data-related or sequence-related, you'll still need to pause the program at some point and examine what's happening in detail; that's what a breakpoint does (it breaks at a certain point).

In the Buggy Program example, let's set a breakpoint just before the user enters the two numbers that the program thinks are equal. Move to that line (cin >> a >> b;) and either right-click and select "Insert/Remove Breakpoint" or press the F9 key.

A red dot will appear next to the line, indicating a breakpoint is set:


Run the program in Debug mode, and the output window will appear, but the compiler window will 'jump' to the foreground and a yellow arrow will be pointing to the line with your breakpoint.

The yellow arrow means that statement will execute next! That is, it has not executed yet. In this case it's rather obvious since you know you didn't enter two numbers, but in more complicated code it might get confusing.



Also, notice the word [break] in Visual Studio's title bar. This means the program is in a "break" state and is not executing any code. Other times you might be looking at the compiler while a statement is busy executing.


[size=+1]Watches
Once you've identified the general part of the program where the trouble lies ("the part that asks for two numbers and compares them") and you've setup a starting breakpoint, you can start checking for data problems (one of the two types of problems you might have).

The "Watch" window lets you watch the contents of any variables you select as your program executes. Thus, if the value changes in an unexpected way you'll see it as it happens. If the Watch window isn't already open, open it from the View menu (Debug Windows > Watch), or by clicking the "Watch" icon in the toolbar, or by pressing Alt+3


If there are already some variables entered, just click each one and press the Delete key to clear it. The two variables involved in this particular problem are a and b. Click the Watch window and press "a" (a text box will appear when you start typing), then Enter to add a to your Watch list:


Add b the same way and you can examine their values:


These two numbers (while coincidentally equal) are completely useless -- they mean only that a and b haven't been initialized (remember, the 'cin' statement hasn't executed yet!) Extremely negative numbers like this are almost sure signs of uninitialized variables.

To determine what's really going wrong, it's necessary to execute some more code.

[size=+1]Stepping
Just checking the values of variables at one point doesn't usually reveal a data problem. Rather, they must be watched over several lines of code to obvserve when they change.

To execute a line of code, click the "Step Over" icon in the toolbar (also found in the Debug menu) or press F10. Doing this causes the 'cin' to execute. This is not the same thing as "Step Into" which is covered later.

At first it looks like nothing happens. Rather, the change is just subtle. Looking at the title bar, the "[break]" has changed to "[run]":


This means the program is actually doing something. You're just looking at the Visual Studio window still, so you don't see it! Switch to your program's window and you'll see it's waiting for your two numbers.

As soon as you enter both numbers, Visual Studio will 'jump' back to the foreground, and the yellow arrow will have moved. (The "[break]" will be back in the title bar too). You told it to execute only that one statement, and as soon as it finished executing it paused again.


Examine the Watch window. The values for both a and b are in red, which means they changed during the most recent execution. Since you're only watching two variables this seems obvious, but if you had many variables and you were watching them over many lines of code, such an "obvious" indicator can be very valuable.


Clearly the values are as expected -- the same two values "the user" entered. So that particular line isn't the problem. "Step Over" the next line (which won't require any user input) and again examine the Watch window.


The value of a is still in red, which means it changed during the most recent execution. The fact that it changed before doesn't matter anymore; it's red because it changed during the execution of the "if ... " line. (Notice that b is black again because it did not change).

Not only did the value of a change, but clearly it changed to be equal to b which explains the problem. Examine the line you just executed, and the error should be fairly obvious -- the condition of the 'if' is (a = b) rather than (a == b). Rather than having to stare at the entire program, the debugger showed you exactly what line was "broken."

[size=+1]Stopping the Debugger
When you've found a problem to correct, it may be tempting to press Ctrl+C in your program window to end the program. DON'T DO THAT! If you end your program, and then try to end the debugger, Visual Studio fails and closes.

Instead, select "Stop Debugging" from the Debug menu or on the toolbar or press Shift+F5. That will both end your program and end the debugger, which makes Visual Studio happy.

[size=+1]Step Into
To examine the second bug in the "Buggy Program" example, some code inside a function may or may not need to be examined. Sometimes you can assume a certain function is working correctly, so it would be a waste of time to step through it. (Ideally you'll have tested certain functions before you try writing others; a lot of the time you'll know the older functions are still working). (For example, you should never step into cin.get() because even if it wasn't working, there'd be nothing you could do.

When you don't want to step through an entire function, use the "Step Over" command. When you do want to step into a function, use the "Step Into" command.

In the "Buggy Program" example, let's assume again that the value for 'c' is stored correctly and set a breakpoint on the next line -- the one that calls the ToPercent() function. Then run the program until that point is reached.

In the Watch window, you can delete the two Watches that are set (a and b) by clicking on each line and pressing "Delete" (on the keyboard), or just type over the existing names. Enter 'c' and 'cAsPercent'.


'cAsPercent' clearly has garbage data, but it hasn't been assigned yet, so that doesn't matter. If you step over the function call, the value of 'cAsPercent' changes:


Whatever is happening in ToPercent() isn't working right, since it's returning a 0! Unfortunately, the entire function has already executed. It's far more hassle than it's worth to "Undo" the execution of a line; instead either click the "Restart" toolbar icon, or "Stop Debugging" and then run it again.

This time, step into ToPercent() and add 'decimal' and 'result' to the Watch window (the two variables local to ToUpper(). Notice also that 'c' and 'cAsPercent' have error messages now -- they belong to main() so you can't inspect their values if you're in ToPercent()!


You can step over or into the next line, since no functions are called; when you do the value of 'result' becomes 0! Looking closely at the line, 'decimal' is being truncated to an int before it's multiplied.

[size=+1]Stepping Into Assembly
Some statements that don't look like function calls really are -- the most common being cin >> variable; and cout << "Text"; Be careful when you "Step Into" lines involving cout, cin, or other system functions!

If you do, you'll get a prompt to find the source code for those functions:


If you click Cancel (which is what you should do), the Disassembly window will appear:


Not only is this not your source code, it's not even C++! Obviously this isn't somewhere fun to be. Performing a "Run to Cursor" is usually the easiest way to get out of this situation.

[size=+1]Run to Cursor
A "Run to Cursor" is like a temporary breakpoint. Right-click on the line just after the one you accidently steped into and select "Run to Cursor" (or use the toolbar button). This will tell the program to run until it gets to that point unless another breakpoint is encountered. If one is encountered, the program will break like normal and will not remember where the cursor was.

The "Run to Cursor" command is particularly good at starting the debugger (perform a "Run to Cursor" before you've started the debugger to get to the point you want to start debugging), and getting out of the Disassembly window. It's not very good if you already have a lot of breakpoints already set.

[size=+1]A "Bug-Free" Program
In reality, no program is ever truly bug-free, but the two obvious problems with the "Buggy Program" example have been resolved:


[size=+1]The "Bug-Free" Code
#include <iostream.h>#include <stdlib.h>int ToPercent (float decimal);void main() {        int a, b;        float c;        int cAsPercent;        cout << "Enter two Integers >";        cin >> a >> b;        if (a == b) cout << "They are Equal!\n";        else if (a > b) cout << "The first one is bigger!\n";        else cout << "The second one is bigger!\n";        cout << "Enter a Decimal to be Converted to Percent >";        cin >> c;        cAsPercent = ToPercent(c);        cout << "That's " << cAsPercent << "%\n";        cout << endl << endl;        system("pause");}/* ToPercent():Converts a given float (eg 0.9) to a percentage (90).*/int ToPercent (float decimal) {        int result;        result = int(decimal * 100);        return result;}[size=+1]More Practice
The best way to learn how these tools work is to use them. Although you'll probably learn best using the debugger with your own code, I've provided a sample program that you can use to practice. The program has a few obvious problems (run it once to see -- none of the three sections works as described) that can be solved using the debugger. Even if you can identify the bugs just from looking at the code, try running the debugger anyway to practice. Examine variables, step through the code, and see if you can find the line on which each problem occurs.

Practice
您需要登录后才可以回帖 登录 | 申请新用户

本版积分规则

小黑屋|手机版|Archiver|守望轩 ( 湘ICP备17013730号-2 )|网站地图

GMT+8, 2018-9-25 19:44 , Processed in 0.029528 second(s), 17 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表