
Beginning Visual C++ 2005 (2006) [eng]-1
.pdf
Debugging Techniques
How can copying from the object to another array corrupt the object, especially because pSurname is passed as an argument for a const parameter? You need to look at the address stored in pName for a clue. Compare it with the address contained in the this pointer. The difference is only 20 bytes-they could hardly be closer really! The address calculation for the position in pName is incorrect, simply because you forgot that copying a space to overwrite the terminating ‘\0’ in the pName array means that strlen(pName) can no longer calculate the correct length of pName. The whole problem is caused by the statement:
pName[strlen(pName)] = ‘ ‘; |
// Append a space |
This is overwriting the ‘\0’ and thus making the subsequent call to strlen() produce an invalid result.
This code is unnecessarily messy anyway-using the library function strcat() to catenate a string is much better than using strcpy(), as it renders all this pointer modification unnecessary. You should rewrite the statement as:
strcat(pName, “ “); |
// Append a space |
Of course, the subsequent statement also needs to be changed to:
return strcat(pName, pSurname); |
// Append second name and return total |
With these changes you can recompile and give it another go. The program appears to run satisfactorily as you can see from the output:
Name constructor called.
Name::getName() called.
The name is Ivor Horton
Name::getNameLength() called.
Name::getName() called.
The name is Ivor Horton
Press any key to continue . . .
Getting the right output does not always mean that all is well, and it certainly isn’t in this case. You get the message box displayed by the debug library shown in Figure 10-17 indicating that the stack is corrupted.
Figure 10-17
589

Chapter 10
The following code shows where the problem lies:
int main(int argc, char* argv[]) |
|
{ |
|
Name myName(“Ivor”, “Horton”); |
// Try a single object |
//Retrieve and store the name in a local char array char theName[10];
cout << “\nThe name is “ << myName.getName(theName);
//Store the name in an array in the free store char* pName = new char[myName.getNameLength()]; cout << “\nThe name is “ << myName.getName(pName);
cout << endl; return 0;
}
Both the shaded lines are in error. The first shaded line provides an array of 10 characters to store the name. In fact, 12 are required: 10 for the two names, one for the space, and one for ‘\0’ at the end. The second shaded line should add 1 to the value returned by the getNameLength() function to allow for the ‘\0’ at the end. Thus, the code in main() should be:
int main(int argc, char* argv[]) |
|
{ |
|
Name myName(“Ivor”, “Horton”); |
// Try a single object |
//Retrieve and store the name in a local char array char theName[12];
cout << “\nThe name is “ << myName.getName(theName);
//Store the name in an array in the free store char* pName = new char[myName.getNameLength()+1]; cout << “\nThe name is “ << myName.getName(pName);
cout << endl; return 0;
}
There’s a more serious problem in the definition getNameLength() member of the class. It omits to add 1 for the space between the first and second names, so the value returned is always one short. The definition should be:
int Name::getNameLength() const
{
#ifdef FUNCTION_TRACE
// Trace function calls
cout << “\nName::getNameLength() called.”; #endif
return strlen(pFirstname)+strlen(pSurname)+1;
}
That’s not the end of it by any means. You may have already spotted that your class still has serious errors, but press on with testing to see if they come out in the wash.
590

Debugging Techniques
Testing the Extended Class
Based on the output, everything is working, so its time to add the definitions for the overloaded comparison operators to the Name class. I’ll assume this is a new Win32 console project, Ex10_02. To implement the comparison operators for Name objects you can use the comparison functions declared in the <cstring> header. Start with the ‘less than’ operator:
// Less than operator
bool Name::operator<(const Name& name) const
{
int result = strcmp(pSurname, name.pSurname); if(result < 0)
return true;
if(result == 0 && strcmp(pFirstname, name.pFirstname) < 0) return true;
else
return false;
}
You can now define the > operator very easily in terms of the < operator:
// Greater than operator
bool Name::operator>(const Name& name) const
{
return name > *this;
}
For determining equal names you use the strcmp() function from the standard library again:
// Equal to operator
bool Name::operator==(const Name& name) const
{
if(strcmp(pSurname, name.pSurname) == 0 &&
strcmp(pFirstname, name.pFirstname) == 0)
return true; else
return false;
}
Now extend the test program. You can create an array of Name objects, initialize them in some arbitrary way, and then compare the elements of the array using your comparison operators for a Name object. Here’s main() along with a function, init(), to initialize a Name array:
// Ex10_02.cpp : Extending the test operation
#include <iostream> using namespace std; #include “Name.h”
// Function to initialize an array of random names void init(Name* names, int count)
{
char* firstnames[] = { “Charles”, “Mary”, “Arthur”, “Emily”, “John”};
591

Chapter 10
int firstsize = sizeof (firstnames)/sizeof(firstnames[0]);
char* secondnames[] = { “Dickens”, “Shelley”, “Miller”, “Bronte”, “Steinbeck”}; int secondsize = sizeof (secondnames)/sizeof(secondnames[0]);
char* first = firstnames[0]; char* second = secondnames[0];
for(int i = 0 ; i<count ; i++)
{
if(i%2)
first = firstnames[i%firstsize]; else
second = secondnames[i%secondsize]; |
|
names[i] = Name(first, second); |
|
} |
|
} |
|
int main(int argc, char* argv[]) |
|
{ |
|
Name myName(“Ivor”, “Horton”); |
// Try a single object |
//Retrieve and store the name in a local char array char theName[12];
cout << “\nThe name is “ << myName.getName(theName);
//Store the name in an array in the free store char* pName = new char[myName.getNameLength()+1]; cout << “\nThe name is “ << myName.getName(pName);
const int arraysize = 10; |
|
Name names[arraysize]; |
// Try an array |
// Initialize names |
|
init(names, arraysize); |
|
// Try out comparisons |
|
char* phrase = 0; |
// Stores a comparison phrase |
char* iName = 0; |
// Stores a complete name |
char* jName = 0; |
// Stores a complete name |
for(int i = 0; i < arraysize ; i++) |
// Compare each element |
{ |
|
iName = new char[names[i].getNameLength()+1]; // Array to hold first name for(int j = i+1 ; j<arraysize ; j++) // with all the others
{
if(names[i] < names[j]) phrase = “ less than “;
else if(names[i] > names[j]) phrase = “ greater than “;
else if(names[i] == names[j]) // Superfluous - but it calls operator==() phrase = “ equal to “;
jName = new char[names[j].getNameLength()+1]; // Array to hold second name cout << endl << names[i].getName(iName) << “ is” << phrase
592

Debugging Techniques
<< names[j].getName(jName);
}
}
cout << endl; return 0;
}
The init() function picks successive combinations of first and second names from the array of names to initialize the array Name objects. Names repeated after 25 have been generated, but you need only 10 here.
Finding the Next Bug
If you start the program under the control of the debugger using the Start Debugging button on the Debug toolbar it fails again. The message box shown in Figure 10-18 is displayed.
Figure 10-18
The message box indicates you have exceeded the capacity of the stack memory available and if you select the Break button the Call Stack window tells you what is wrong. You have successive calls of the operator>() function so it must be calling itself. If you look at the code, you can see why: a typo. The single line in the body of the function should be:
return name < *this;
You can fix that, recompile, and try again. This time it works correctly, but unfortunately the class is still defective. It has a memory leak that exhibits no symptoms here, but in another context could cause mayhem. Memory leaks are hard to detect ordinarily, but you can get some extra help from Visual C++ 2005.
Debugging Dynamic Memor y
Allocating memory dynamically is a potent source of bugs and perhaps the most common bugs in this context are memory leaks. Just to remind you, a memory leak arises when you use the new operator to allocate memory, but you never use the delete operator to free it again when you are done with it.
Apart from just forgetting to delete memory that you have allocated, you should particularly be aware
593

Chapter 10
that non-virtual destructors in a class hierarchy can also cause the problem-because it can cause the wrong destructor to be called when an object is destroyed, as you have seen. Of course, when your program ends, all the memory is freed; however, while it is running, it remains allocated to your program. Memory leaks present no obvious symptoms much of the time, maybe never in some cases, but memory leaks are detrimental to the performance of your machine because memory is being occupied to no good purpose. Sometimes, it can result in a catastrophic failure of the program when all available memory has been allocated.
For checking your program’s use of the free store, Visual C++ 2005 provides a range of diagnostic routines; these use a special debug version of the free store. These are declared in the header crtdbg.h. All calls to these routines are automatically removed from the release version of your program, so you don’t need to worry about adding preprocessor controls for them.
Functions Checking the Free Store
Here’s an overview of what’s involved in checking free store operations and how memory leaks can be detected. The functions declared in ctrdbg.h check the free store using a record of its status stored in a structure of type _CrtMemState. This structure is relatively simple and is defined as:
typedef struct _CrtMemState
{
struct _CrtMemBlockHeader* pBlockHeader; // Ptr to most recently allocated block
unsigned long lCounts[_MAX_BLOCKS]; |
// Counter for each type of block |
||
unsigned long lSizes[_MAX_BLOCKS];// |
Total bytes allocated in each block type |
||
unsigned long |
lHighWaterCount; |
// |
The most bytes allocated at a time up to now |
unsigned long |
lTotalCount; |
// |
The total bytes allocated at present |
} _CrtMemState;
You won’t be concerned directly with the details of the state of the free store because you are using functions that present the information in a more readable form. There are quite a few functions involved in tracking free store operations but you will only look at the five most interesting ones. These provide you with the following capabilities:
To record the state of the free store at any point
To determine the difference between two states of the free store
To output state information
To output information about objects in the free store
To detect memory leaks
Here are the declarations of these functions together with a brief description of what they do:
void _CrtMemCheckpoint(_CrtMemState* state);
This stores the current state of the free store in a _CrtMemState structure. The argument you pass to the function is a pointer to a _CrtMemState structure in which the state is to be recorded.
int _CrtMemDifference(_CrtMemState* stateDiff, const _CrtMemState* oldState,
const _CrtMemState* newState);
594

Debugging Techniques
This function compares the state specified by the third argument, with a previous state that you specify in the second argument. The difference is stored in a _CrtMemState structure that you specify in the first argument. If the states are different, the function returns a non-zero value (true); otherwise, 0 (false) is returned.
void _CrtMemDumpStatistics(const _CrtMemState* state);
This dumps information about the free store state specified by the argument to an output stream. The state structure pointed to by the argument can be a state that you recorded using _CrtMemCheckpoint() or the difference between two states produced by _CrtMemDifference().
void _CrtMemDumpAllObjectsSince(const _CrtMemState* state);
This function dumps information on objects allocated in the free store, since the state of the free store specified by the argument; this has been recorded by an earlier call in your program to _CrtMemCheckpoint(). If you pass null to the function, it dumps information on all objects allocated since the start of execution of your program.
int _CrtDumpMemoryLeaks();
This is the function you need for the example as it checks for memory leaks and dumps information on any leak that is detected. You can call this function at any time, but a very useful mechanism can cause the function to be called automatically when your program ends. If you enable this mechanism, you get automatic detection of any memory leaks that occurred during program execution, so see how you can do that.
Controlling Free Store Debug Operations
You control free store debug operations by setting a flag, _crtDbgFlag, which is of type int. This flag incorporates five separate control bits, including one to enable automatic memory leak checking. You specify these control bits using the following identifiers:
_CRTDBG_ALLOC_MEM_DF |
When this bit is on, it turns on debug allocation so the |
|
free store state can be tracked. |
_CRTDBG_DELAY_FREE_MEM_DF |
When this is on, it prevents memory from being freed by |
|
delete, so that you can determine what happens under |
|
low-memory conditions. |
_CRTDBG_CHECK_ALWAYS_DF |
When this is on, it causes the _CrtCheckMemory() func- |
|
tion to be called automatically at every new and delete |
|
operation. This function verifies the integrity of the free |
|
store, checking, for example, that blocks have not been |
|
overwritten by storing values beyond the range of an |
|
array. A report is output if any defect is discovered. This |
|
slows execution but catches errors quickly. |
_CRTDBG_CHECK_CRT_DF |
When this is on, the memory used internally by the run- |
|
time library is tracked in debug operations. |
_CRTDBG_LEAK_CHECK_DF |
Causes leak checking to be performed at program exit by |
|
automatically calling _CrtDumpMemoryLeaks(). You |
|
only get output from this if your program has failed to |
|
free all the memory that it allocated. |
|
|
595

Chapter 10
By default, the _CRTDBG_ALLOC_MEM_DF bit is on, and all the others are off. You must use the bitwise operators to set and unset combinations of these bits. To set the _crtDbgFlag flag you pass a flag of type int to the _CrtDbgFlag() function that implements the combination of indicators that you require. This puts your flag into effect and returns the previous status of _CrtDbgFlag. One way to set the indicators you want is to first obtain the current status of the _crtDbgFlag flag. Do this by calling the _CrtSetDbgFlag() function with the argument _CRTDBG_REPORT_FLAG as follows:
int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); |
// Get current flag |
You can then set or unset the indicators by combining the identifiers for the individual indicators with this flag using bitwise operators. To set an indicator on, you OR the indicator identifier with the flag. For example, to set the automatic leak checking indicator on, in the flag, you could write:
flag |= _CRTDBG_LEAK_CHECK_DF;
To turn an indicator off, you must AND the negation of the identifier with the flag. For example, to turn off tracking of memory that is used internally by the library, you could write:
flag &= ~_CRTDBG_CHECK_CRT_DF;
To put your new flag into effect, you just call _CrtSetDbgFlag() with your flag as the argument:
_CrtSetDbgFlag(flag);
Alternatively, you can OR all the identifiers for the indicators that you want, together, and pass the result as the argument to _CrtSetDbgFlag(). If you just want to leak check when the program exits, you could write:
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF);
If you need to set a particular combination of indicators, rather than setting or unsetting bits at various points in your program, this is the easiest way to do it. You are almost at the point where you can apply the dynamic memory debugging facilities to our example. You just need to look at how you determine where free store debugging output goes.
Free Store Debugging Output
The destination of the output from the free store debugging functions is not the standard output stream; by default it goes to the debug message window. If you want to see the output on stdout you must set this up. There are two functions involved in this: _CrtSetReportMode(), which sets the general destination for output, and _CrtSetReportFile(), which specifies a stream destination specifically. The _CrtSetReportMode() function is declared as:
int _CrtSetReportMode(int reportType, int reportMode);
There are three kinds of output produced by the free store debugging functions. Each call to the _CrtSetReportMode() function sets the destination specified by the second argument for the output type specified by the first argument. You specify the report type by one of the following identifiers:
596

|
|
Debugging Techniques |
|
|
|
|
_CRT_WARN |
Warning messages of various kinds. The output when a memory |
|
|
leak is detected is a warning. |
|
_CRT_ERROR |
Catastrophic errors that report unrecoverable problems. |
|
_CRT_ASSERT |
Output from assertions (not output from the assert() function |
|
|
that I discussed earlier). |
|
|
|
The crtdbg.h header defines two macros, ASSERT and ASSERTE, that work in much the same way as the assert() function in the standard library. The difference between these two macros is that ASSERTE reports the assertion expression when a failure occurs, whereas the ASSERT macro does not.
You specify the report mode by a combination of the following identifiers:
_CRTDBG_MODE_DEBUG |
This is the default mode, which sends output to a debug string |
|
that you see in the debug window when running under control of |
|
the debugger. |
_CRTDBG_MODE_FILE |
Output is to be directed to an output stream. |
_CRTDBG_MODE_WNDW |
Output is presented in a message box. |
_CRTDBG_REPORT_MODE |
If you specify this, the _CrtSetReportMode() function just |
|
returns the current report mode. |
|
|
To specify more than one destination, you simply OR the identifiers using the | operator. You set the destination for each output type with a separate call of the _CrtSetReportMode() function. To direct the output when a leak is detected to a file stream, you can set the report mode with the following statement:
CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
This just sets the destination generically as a file stream. You still need to call the _CrtSetReportFile() function to specify the destination specifically.
The _CrtSetReportFile() function is declared as:
_HFILE _CrtSetReportFile(int reportType, _HFILE reportFile);
The second argument here can either be a pointer to a file stream (of type _HFILE), which I will not go into further, or can be one of the following identifiers:
_CRTDBG_FILE_STDERR |
Output is directed to the standard error stream, stderr. |
_CRTDBG_FILE_STDOUT |
Output is directed to the standard output stream, stdout. |
_CRTDBG_REPORT_FILE |
If you specify this argument, the _CrtSetReportFile() function |
|
will just return the current destination. |
|
|
597

Chapter 10
To set the leak detection output to the standard output stream, you can write:
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
You now have enough knowledge of the free store debug routines to try out leak detection in your example.
Try It Out |
Memory Leak Detection |
Even though you have set the project settings to direct the standard output stream to a file, it would be a good idea to reduce the volume of output, so reduce the size of the names array to five elements. Here’s the new version of main() for Ex10_02 to use the free store debug facilities in general and leak detection in particular:
int main(int argc, char* argv[])
{
// Turn on free store debugging and leak-checking bits
_CrtSetDbgFlag( _CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF );
// Direct warnings to stdout _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
Name myName(“Ivor”, “Horton”); |
// Try a single object |
//Retrieve and store the name in a local char array char theName[12];
cout << “\nThe name is “ << myName.getName(theName);
//Store the name in an array in the free store char* pName = new char[myName.getNameLength()+1]; cout << “\nThe name is “ << myName.getName(pName);
const int arraysize = 5; |
|
Name names[arraysize]; |
// Try an array |
// Initialize names |
|
init(names, arraysize); |
|
// Try out comparisons |
|
char* phrase = 0; |
// Stores a comparison phrase |
char* iName = 0; |
// Stores a complete name |
char* jName = 0; |
// Stores a complete name |
for(int i = 0; i < arraysize ; i++) |
// Compare each element |
{ |
|
iName = new char[names[i].getNameLength()+1]; // Array to hold first name for(int j = i+1 ; j<arraysize ; j++) // with all the others
{
if(names[i] < names[j]) phrase = “ less than “;
else if(names[i] > names[j]) phrase = “ greater than “;
598