At the bottom is an example of how these tools may be used outside of the ECE 250 tester-driver environment.
ECE 250 students are not required to know this.
Memory management is tracked through the an overriding of the new, delete, new[], and delete[] operators. In order for the memory tracking to be as uninvasive as possible, it is necessary to override the global instances of the four operators. Each of these uses either malloc or free to allocate and de-allocate memory. In addition to memory allocation, it tracks information about the allocation in a hash table.
Memory allocation and de-allocation is tracked through a hash table.
The hash function is based on the address of the pointer being returned. Because almost all memory allocated with be 4 bytes in size (int and pointers (the testing machine is a 32-bit machine), we will ignore the two least-significant bits.
With each memory allocation, we must store certain pieces of information which allow us to correctly track what the student has done. We will store the following four fields:
The hash table class also stores two additional integers: int total_memory_allocated and int total_memory_deleted. Each time a new entry is entered into the hash table, the size is added to the first, while each time memory is de-allocated, the size associated with the address is added to the second.
The class member bool record, initially set to false may be used to either turn on or turn off memory tracking. This allows us to avoid tracking memory allocated and de-allocated by library functions such as cin. This variable may be appropriately set using:
Two additional member functions document the memory allocated:
Memory allocated minus memory deallocated: 0
SUMMARY OF MEMORY ALLOCATION: Memory allocated: 28 Memory deallocated: 8 INDIVIDUAL REPORT OF MEMORY ALLOCATION: Memory allocated at address: 0x2a800 Bytes allocated: 8 Allocated with new and was appropriately deallocated. Memory allocated at address: 0x29a28 Bytes allocated: 12 Allocated with new but was appropriately NOT deallocated. Memory allocated at address: 0x2a7f0 Bytes allocated: 8 Allocated with new but was appropriately NOT deallocated.
Each time the operators new and new[] are called, the data is inserted into the hash table.
Each time the operators delete and delete[] are called, the data is not removed from the hash table, but rather, the deleted flag is set to true. This allows the student to summarize all memory allocations and deallocations.
Note: it may occur that de-allocated memory is reassigned by the operating system. In this case, the deleted flag is set back to false and the new information is recorded. This may create an inconsistency between the total number of bytes allocated and the sum of the bytes allocated with each individual memory allocation.
For those interested, the overridden new operator is called with an argument (an unsigned integer) equal to the number of bytes requested. The new[] operator is called with an argument (an unsigned integer) eqaul to the total number of bytes required for the full array. The overridden delete and delete[] operators are called with a generic pointer argument (void *).
It is possible to override these operators within each class submitted by the students, however, it is felt that this would unnecessarily complicate the projects and would detract from the implementation of the data structures and algorithms. Even if they were provided with the classes, this would essentially duplicate code which students would use for each project.
The following demonstates how you can use this memory tracking tool in any program. You can download the file example.cpp and the header file dwharder/Algorithms_and_Data_Structures.h. As well as demonstrating the functionality, this sample code gives three possible failures which may be the result of improper use of delete or delete[].
Program 1. Use of the dwharder/Algorithms_and_Data_Structures.h header for memory tracking.
#include "dwharder/Algorithms_and_Data_Structures.h" int main() { // this allocation is not tracked int *x = new int[33]; dwharder/Algorithms_and_Data_Structures::allocation_table.start_recording(); // these are all tracked int *a = new int(3); int *b = new int[100]; int *c = new int[1000]; dwharder/Algorithms_and_Data_Structures::allocation_table.summary(); delete a; dwharder/Algorithms_and_Data_Structures::allocation_table.summary(); delete [] b; dwharder/Algorithms_and_Data_Structures::allocation_table.summary(); delete [] c; dwharder/Algorithms_and_Data_Structures::allocation_table.summary(); dwharder/Algorithms_and_Data_Structures::allocation_table.details(); // Failure 1 // int *z1 = new int[100]; // delete z1; // Wrong delete, use delete[] // Failure 2 // int *z2 = new int[100]; // delete [] z2; // delete [] z2; // Deleting same memory location twice // Failure 3 // int *z3 = (int *) 0x0F13AC79; // random address // delete [] z3; // deleting memory not allocated return 0; }
Copyright ©2006 by Douglas Wilhelm Harder. All rights reserved.