📄 chxc.htm
字号:
<P><B>Examining Variable Values</B></P>
<P>When you set a breakpoint and debug the program, everything proceeds normally until the breakpoint line of code is about to execute. Then Developer Studio comes up on top of your application, with some extra windows in the display and a yellow arrow in
the red margin dot that indicated your breakpoint, as shown in Figure C.3. This shows you the line of code that is about to execute.</P>
<A HREF="GGfig03.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig03.gif"><b>Fig. C.3</b></A>
<P><I>A yellow arrow indicates the line of code about to execute.</I></P>
<P>Move the mouse over a variable name, like <font color="#008000">color</font> or <font color="#008000">horizcenter</font>. A DataTip appears, telling you the current value of this variable. You can check as many local variables as you want like this,
then continue executing and check them again. But there are other ways to examine variable values.</P>
<P>You could click on the variable and choose Debug, QuickWatch, or click the QuickWatch button (a pair of glasses) on the toolbar. This brings up the QuickWatch window, which shows you the value of a variable or expression and lets you add it to the
watch window if you wish. You're probably wondering why anyone uses this feature now that DataTips will show you a variable's value without even clicking. DataTips can't handle expressions, even simple ones like <font
color="#008000">dlg.m_horizcenter</font>, but QuickWatch can, as you see in Figure C.4. You can also change a variable's value with this dialog box, to recover from horrible errors and see what happens.</P>
<A HREF="GGfig04.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig04.gif"><b>Fig. C.4</b></A>
<P><I>The QuickWatch dialog box evaluates expressions. You can add them to the </I><I>Watch window by clicking Add </I><I><U>W</U></I><I>atch.</I></P>
<P>Figure C.5 shows a debug session after running forward a few lines from the original breakpoint (You'll see how to do that in a moment.) The Watch and Variable windows have been undocked to show more clearly which is which, and two watches have been
added: one for <font color="#008000">horizcenter</font> and one for <font color="#008000">dlg.m_horizcenter</font>. The program is paused immediately after the user clicked OK on the Options dialog, and in this case the user changed the string, the color,
and both kinds of centering.</P>
<A HREF="GGfig05.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig05.gif"><b>Fig. C.5</b></A>
<P><I>The Watch window and the Variable window make it easy to know the values </I><I>of all your variables.</I></P>
<P>The Watch window simply shows the values of the two variables that were added to it. <font color="#008000">horizcenter</font> is still true (1), because the line of code that sets it has not yet been executed. <font
color="#008000">dlg.m_horizcenter</font> is false (0), because the user deselected the check box associated with the member variable. (Dialogs, controls, and associating controls with member variables are all discussed in <A HREF="index02.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index02.htm"
target="text">Chapter 2</A>, "Dialog Boxes and Controls.")</P>
<P>The Variables window has a lot more information in it, which sometimes makes it harder to use. The local variable <font color="#008000">dlg</font>, and the pointer to the object for whom this member function was invoked, <font
color="#008000">this</font>, are both in the Variables window in tree form: click on a + to expand the tree and on a - to collapse it. In addition, the return value from DoModal, 1, is displayed.</P>
<P>At the top of the Variables window is a drop-down box labeled Context. Dropping it down shows how control got here: it lists the names of a series of functions. The top entry is the function in which the line about to be executed is contained, <font
color="#008000">CShowStringDoc::OnToolsOptions().</font> The second entry is the function that called this one, <font color="#008000">DispatchCmdMsg</font>, which dispatches command messages. <A HREF="index04.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index04.htm" target="text">Chapter 4</A>,
"Messages and Commands," introduces commands and messages and discusses the way that control passes to a message-handling function like <font color="#008000">OnToolsOptions</font>. Here, the debugger gives proof of this process right before your
eyes. Double-click on any function name in the drop-down box and the code for that function is displayed, you can look at variables local to that function, and so on.</P>
<P>The Call Stack window, shown in Figure C.6, is a little easier to examine than the drop-down box in the Variables window, and it shows the same information. As well as the function names, you can see the parameters that were passed to each function.
You may notice the number 32771 recurring in most of the function calls. Choose <U>V</U>iew, Resource S<U>y</U>mbols and you'll see that 32771 means <font color="#008000">ID_TOOLS_OPTIONS</font>, the resource ID associated with the menu item <U>T</U>ools,
<U>O</U>ptions in ShowString (see Figure C.7.)</P>
<A HREF="GGfig06.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig06.gif"><b>Fig. C.6</b></A>
<P><I>The Call Stack window shows how you got here.</I></P>
<A HREF="GGfig07.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig07.gif"><b>Fig. C.7</b></A>
<P><I>The number 32771 corresponds to </I><I><font color="#008000">ID_TOOLS_OPTIONS</font></I><I>.</I></P>
<P><B>Stepping Through Code</B></P>
<P>Double-clicking a function name in the call stack or the context drop-down box of the Variables window doesn't make any code execute: it simply gives you a chance to examine the local variables of the functions that called the function that is now
executing. After you've looked at all the variables you want to look at, it's time to move on. While there are items on the Debug menu to Step Over, Step Into, and so on, most developers use the toolbar buttons or the keyboard shortcuts. The Debug can be
seen in Figures C.1, C.3, and C.5. Pause your mouse over each button to see the command it is connected to and a reminder of the keyboard shortcut. For example, the button showing an arrow going down into a pair of brace brackets is Step Into, and the
shortcut key is F11.</P>
<P>As you move through code, the yellow arrow in the margin moves with you, to show which line is about to execute. Whenever the program is paused you can add or remove breakpoints, examine variables, or resume execution. These are the mechanics of
debugging.</P>
<P><B>Other Debug Windows</B></P>
<P>Three debug windows have not yet been discussed: Memory, Registers, and Disassembly. These windows provide a level of detail rarely required in ordinary debugging. With each release of Visual C++, the circumstances under which these windows are needed
dwindle. For example, the Registers window used to be the only way to see the value just returned from a function call. Now that information is in the Variables window in a more accessible format.</P>
<P><B>The Memory Window</B></P>
<P>This window, shown in Figure C.8, shows you the hex values in every byte of the memory space from 0x00000000 to 0xFFFFFFFF. It's a very long list, which makes the dialog box hard to scroll in—use the Address box to enter an address that interests
you. Typically, these addresses are copied (through the clipboard, not by hand) from the Variables window. It is a handy way to look through a large array or to track down subtle platform-dependent problems.</P>
<A HREF="GGfig08.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig08.gif"><b>Fig. C.8</b></A>
<P><I>You can examine raw memory, though you'll rarely need to.</I></P>
<P><B>The Registers Window</B></P>
<P>If you are debugging at the assembler level, it may be useful to examine the registers. Figure C.9 shows the Registers window. This shot was taken at the same point of execution as Figure C.5, and you can see that the EAX register contains the value 1,
which is the return value from <font color="#008000">DoModal().</font></pre>
<A HREF="GGfig09.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig09.gif"><b>Fig. C.9</b></A>
<P><I>All the registers are available to examine or change.</I></P>
<P><B>The Disassembly window</B></P>
<P>By default, the Disassembly window comes up full screen, replacing the C++ code in the main working area. You can see the assembly language statements generated for your C++ code, as shown in Figure C.10. Debugging at the assembly level is beyond the
scope of this book.</P>
<A HREF="GGfig10.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig10.gif"><b>Fig. C.10</b></A>
<P><I>You can debug the assembler that was generated for you.</I></P>
<H3><B>Using MFC Tracer</B></H3>
<P>The MFC Tracer utility is a stand-alone application with an integrated menu item in the Developer Studio. To run it, choose <U>T</U>ools, MFC <U>T</U>racer. Figure C.11 shows the Tracer dialog that appears.</P>
<A HREF="GGfig11.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/chxc/GGfig11.gif"><b>Fig. C.11</b></A>
<P><I>A standalone utility simplifies setting trace flags.</I></P>
<P>Tracer doesn't do very much: it's just an easy way to set trace flags that govern the kind of debug output you get. Try setting all the flags on and running ShowString, simply starting it up and shutting it down. Turn off a few flags and see how the
output you get changes.</P>
<P>With all the trace flags on, your application will be slow. Use Tracer to set only the ones you're interested in, while you're interested in them. It's much easier than changing a variable on the fly.</P>
<H3><B>Defining a </B><B><I>Dump</I></B><B> Member Function</B></H3>
<P>All MFC classes have a <font color="#008000">Dump() member</font> function. When things go wrong, some error-handling code calls this function to show you the contents of the object. You can write <font color="#008000">Dump()</font> functions for your
objects, too.</P>
<P>MFC classes inherit <font color="#008000">Dump()</font> from <font color="#008000">Cobject</font>, where it is defined like this:</P>
<pre><font color="#008000">virtual void Dump(CDumpContext& dc ) const;</font></pre>
<P>The keyword <font color="#008000">virtual</font> suggests you should override the method in your derived classes, and <font color="#008000">const</font> indicates that <font color="#008000">Dump()</font> will not modify the object state.</P>
<P>Like trace and assert statements, the <font color="#008000">Dump() </font>member function disappears in a release build. This saves users seeing output they can't deal with, and makes a smaller, faster, release version for you. You have to make this
happen yourself for any <font color="#008000">Dump()</font> function you write, with conditional compilation.</P>
<P>In the header file, declare <font color="#008000">Dump()</font> like this:</P>
<pre><font color="#008000">class CNewClass : public CObject</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000">public:</font></pre>
<pre><font color="#008000"> // other class stuff</font></pre>
<pre><font color="#008000"> #ifdef _DEBUG</font></pre>
<pre><font color="#008000"> virtual void Dump( CDumpContext& dc) const</font></pre>
<pre><font color="#008000"> #endif</font></pre>
<pre><font color="#008000"> // ...</font></pre>
<pre><font color="#008000">};</font></pre>
<P>And in the implementation file, the definition, which includes a code body, might look like this:</P>
<pre><font color="#008000">#include “cnewclass.h”</font></pre>
<pre><font color="#008000">#ifdef _DEBUG</font></pre>
<pre><font color="#008000">void CNewClass::Dump( CDumpContext& dc ) const</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> CObject::Dump( dc ); // Dump parent;</font></pre>
<pre><font color="#008000"> // perhaps dump individual members, works like cout</font></pre>
<pre><font color="#008000"> dc << “member: “ << /* member here */ endl;</font></pre>
<pre><font color="#008000">}</font></pre>
<pre><font color="#008000">#endif</font></pre>
<P>As you see in the code for the <font color="#008000">Dump()</font> function, writing the code is much like writing to standard output with the <font color="#008000">cout</font> object, or serializing to an archive. You are provided with a <font
color="#008000">CDumpContext </font>object called dc, and you send text and values to that object with the <font color="#008000"><<</font> operator. If this is unfamiliar to you, read <A HREF="index08.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index08.htm" target="text">Chapter 8</A>,
"Persistence and File I/O."</P>
<P><B>A Sample Using </B><B><I>CDumpContext</I></B><B>, </B><B><I>CFile</I></B><B>, and </B><B><I>axfDump</I></B></P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -