Valgrind is a command line tool used to debug memory errors and to detect memory leaks.

Memory errors include referencing an index outside the bounds of an array, referencing an uninitialized variable, or dereferencing a null pointer. Many such memory errors might be reported with messages such as "Segmentation fault (core dumped)" or "Timeout monitored command dumped core" when you execute your programs without Valgrind.

A memory leak occurs when heap allocated memory is not deallocated when the space is no longer needed. Valgrind can help detect these issues and might even tell you where in your program these errors occur.

It is important to remember that code may function correctly and still contain memory leaks and errors. Make sure you check your code by running valgrind for each assignment before you submit. Your programs will be tested with valgrind, and we expect them to produce no leaks or errors.

Running valgrind

For the first two assignments, you will use unit_test to run your code. You can read more about the unit_test framework here. unit_test will run valgrind for you!

In general, you can use the following to run valgrind on the executable file compiled from your code:
valgrind [optional flags] <path to your program>
  
If the program you are testing is named a.out and it is located in the current directory, you would run valgrind like this:
  
valgrind ./a.out
  
The output might look something like this:
==640487== Memcheck, a memory error detector
==640487== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==640487== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==640487== Command: ./a.out
==640487== 
<Your Program's Output> Hello world!
==640487== 
==640487== HEAP SUMMARY:
==640487==     in use at exit: 0 bytes in 0 blocks
==640487==   total heap usage: 2 allocs, 2 frees, 73,728 bytes allocated
==640487== 
==640487== All heap blocks were freed -- no leaks are possible
==640487== 
==640487== For lists of detected and suppressed errors, rerun with: -s
==640487== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
  
For more detailed output, you can add the optional flags --leak-check=full --show-leak-kinds=all code>. Thus, you could run:
valgrind --leak-check=full --show-leak-kinds=all ./a.out
  

Interpreting valgrind's Output

There are 3 main parts to valgrind's output:
  1. The Heap Summary tells you the number of bytes in use when the program exits, the number of memory allocations (anytime the new operator is used), the number of frees (anytime the delete or delete [] operators are used), and the total number of bytes allocated.
  2. The Leak Summary tells you what memory your program might have leaked. Anything lost means that some heap allocated memory can no longer be reached by your program. Read more about what a memory leak is below.
  3. The Error Summary tells you how many errors occurred during the execution of your program. Read more about what a memory error is below.
All of valgrind's output is printed on a line beginning with the ==PID==, where PID is some number. Any code printed by your program will appear on a line of its own.

To begin interpreting valgrind, scroll to the top of the output and tackle issues one at a time. Valgrind will separate each issue into its own section of output, and within those sections you can see the functions and lines that were run before the error occurred. Some specific examples are provided below.

What a Memory Leak Looks Like

Let's see what happens when we allocate space for an array of 5 integers on the heap but forget to free it:
int main(int argc, char *argv[])
{
    int *data = new int[5];
}
  
valgrind shows the following:
==418113== Memcheck, a memory error detector
==418113== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==418113== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==418113== Command: ./a.out
==418113== 
==418113== 
==418113== HEAP SUMMARY:
==418113==     in use at exit: 20 bytes in 1 blocks
==418113==   total heap usage: 2 allocs, 1 frees, 72,724 bytes allocated
==418113== 
==418113== LEAK SUMMARY:
==418113==    definitely lost: 20 bytes in 1 blocks
==418113==    indirectly lost: 0 bytes in 0 blocks
==418113==      possibly lost: 0 bytes in 0 blocks
==418113==    still reachable: 0 bytes in 0 blocks
==418113==         suppressed: 0 bytes in 0 blocks
==418113== Rerun with --leak-check=full to see details of leaked memory
==418113== 
==418113== For lists of detected and suppressed errors, rerun with: -s
==418113== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
  
In the Heap Summary, there are 2 allocations and only one free; 1 allocation and 1 free comes from our use of the C++ libraries. These two numbers should always be equal to each other: for every new there should be a corresponding delete!

The Leak Summary shows that we've definitely lost 20 bytes of memory on the heap, which comes from our heap array (5 integers of 4 bytes each). These exact numbers can be helpful in showing us exactly what information we may have lost.

valgrind suggests at the bottom of the output that we run the command again with some additional flags. Doing so, we get this output:
==419224== Memcheck, a memory error detector
==419224== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==419224== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==419224== Command: ./a.out
==419224== 
==419224== 
==419224== HEAP SUMMARY:
==419224==     in use at exit: 20 bytes in 1 blocks
==419224==   total heap usage: 2 allocs, 1 frees, 72,724 bytes allocated
==419224== 
==419224== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
==419224==    at 0x4C39C63: operator new[](unsigned long) (vg_replace_malloc.c:714)
==419224==    by 0x400748: main (main.cpp:14)
==419224== 
==419224== LEAK SUMMARY:
==419224==    definitely lost: 20 bytes in 1 blocks
==419224==    indirectly lost: 0 bytes in 0 blocks
==419224==      possibly lost: 0 bytes in 0 blocks
==419224==    still reachable: 0 bytes in 0 blocks
==419224==         suppressed: 0 bytes in 0 blocks
==419224== 
==419224== For lists of detected and suppressed errors, rerun with: -s
==419224== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
  
Starting from the bottom of the first section of output, we can see that our memory leak begins in the main function of main.cpp, specifically on line 14. The next operation performed is the allocation triggered by the new keyword; since the last line that we wrote was in main, we should start looking for our memory leak there!

Another common memory leak would appear in the "still reachable" section of the Leak Summary. These errors are very similar, and you should look for them in the same ways.

What a Memory Error Looks Like

Now let's look at an example of a memory error. Say you realize you never freed that dynamic array so you add the appropriate delete statement. However, suppose you accidentally delete it twice.
int main(int argc, char *argv[])
{
    int *data = new int[5];
    delete [] data;
    delete [] data;
}
  
This attempts to free memory twice, and error known as a double free. valgrind reports the error:
==422056== Memcheck, a memory error detector
==422056== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==422056== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==422056== Command: ./a.out
==422056== 
==422056== Invalid free() / delete / delete[] / realloc()
==422056==    at 0x4C3C2B5: operator delete[](void*) (vg_replace_malloc.c:1289)
==422056==    by 0x4007C9: main (main.cpp:16)
==422056==  Address 0x5bd0c80 is 0 bytes inside a block of size 20 free'd
==422056==    at 0x4C3C2B5: operator delete[](void*) (vg_replace_malloc.c:1289)
==422056==    by 0x4007AE: main (main.cpp:15)
==422056==  Block was alloc'd at
==422056==    at 0x4C39C63: operator new[](unsigned long) (vg_replace_malloc.c:714)
==422056==    by 0x40078F: main (main.cpp:14)
==422056== 
==422056== 
==422056== HEAP SUMMARY:
==422056==     in use at exit: 0 bytes in 0 blocks
==422056==   total heap usage: 2 allocs, 3 frees, 72,724 bytes allocated
==422056== 
==422056== All heap blocks were freed -- no leaks are possible
==422056== 
==422056== For lists of detected and suppressed errors, rerun with: -s
==422056== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
  
Looking at Heap and Leak Summary, we no longer have any memory leaks! But it seems like our allocs and frees still aren't equal, and we have another error that says something about an invalid free? valgrind again shows us that the error occurred in main, specifically that our erroneous memory was allocated on line 14, freed on line 15, then freed again on line 16. Let's begin looking for our errors there!

For other typical valgrind errors, we recommend that you read this recource: http://cs.ecs.baylor.edu/~donahoo/tools/valgrind/messages.html

Understanding valgrind's Error Messages

Here is another example of a memory error:
==32216== Conditional jump or move depends on uninitialised value(s)
==32216== at 0x4027E5: PirateList::pirateIndex(int) (PirateList.cpp:151)
==32216== by 0x402A21: PirateList::addFriend(int, int) (PirateList.cpp:115)
==32216== by 0x401D90: Hookbook::addFriend(int, int)
==32216== by 0x401A03: run_final_lab_test() (test_hookbook.cpp:46)
==32216== by 0x401253: main (test_hookbook.cpp:27)
  
When reading valgrind error messages, we start from the bottom and work upwards. The flow of the program looks like this:
  1. In the function main, at line 27 in the file test_hookbook.cpp
  2. In the funciton run_final_lab_test, at line 46 in the file test_hookbook.cpp
  3. Function addFriend in the Hookbook class
  4. In the function addFriend of the PirateList at line 115 in the file PirateList.cpp
  5. In the function pirateIndex in the PirateList at line 151 in the file PirateList.cpp
Line 151 of PirateList.cpp is:
        if (priateArray[i].memberID == memID)
  
The above valgrind message is telling us that we are accessing an uninitialized value in that line of code. Either pirateArray[i] itself, or the memberID field, or the local variable memID is uninitialized. You can now do some debugging to figure out which one is causing the problem. valgrind is a very helpful tool, but you still have to use other methods of debugging alongside it.

Let's consider another example of a memory error.
==288950== Memcheck, a memory error detector
==288950== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==288950== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==288950== Command: ./a.out
==288950== 
==288950== Conditional jump or move depends on uninitialised value(s)
==288950==    at 0x408CFE: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950== 
==288950== Use of uninitialised value of size 8
==288950==    at 0x408D05: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950== 
==288950== Use of uninitialised value of size 8
==288950==    at 0x408D1C: _M_dispose (basic_string.h:239)
==288950==    by 0x408D1C: ~basic_string (basic_string.h:672)
==288950==    by 0x408D1C: ~MyClass2 (MyClass1.h:52)
==288950==    by 0x408D1C: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950== 
==288950== Invalid read of size 8
==288950==    at 0x408D1C: _M_dispose (basic_string.h:239)
==288950==    by 0x408D1C: ~basic_string (basic_string.h:672)
==288950==    by 0x408D1C: ~MyClass2 (MyClass1.h:52)
==288950==    by 0x408D1C: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950==  Address 0xa04da7678409ae8 is not stack'd, malloc'd or (recently) free'd
==288950== 
==288950== 
==288950== Process terminating with default action of signal 11 (SIGSEGV)
==288950==  General Protection Fault
==288950==    at 0x408D1C: _M_dispose (basic_string.h:239)
==288950==    by 0x408D1C: ~basic_string (basic_string.h:672)
==288950==    by 0x408D1C: ~MyClass2 (MyClass1.h:52)
==288950==    by 0x408D1C: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950== 
==288950== HEAP SUMMARY:
==288950==     in use at exit: 72,704 bytes in 1 blocks
==288950==   total heap usage: 1 allocs, 0 frees, 72,704 bytes allocated
==288950== 
==288950== LEAK SUMMARY:
==288950==    definitely lost: 0 bytes in 0 blocks
==288950==    indirectly lost: 0 bytes in 0 blocks
==288950==      possibly lost: 0 bytes in 0 blocks
==288950==    still reachable: 72,704 bytes in 1 blocks
==288950==         suppressed: 0 bytes in 0 blocks
==288950== Reachable blocks (those to which a pointer was found) are not shown.
==288950== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==288950== 
==288950== Use --track-origins=yes to see where uninitialised values come from
==288950== For lists of detected and suppressed errors, rerun with: -s
==288950== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
  
The four most useful "blocks" from this valgrind output are these:
==288950== Conditional jump or move depends on uninitialised value(s)
==288950==    at 0x408CFE: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950== 
==288950== Use of uninitialised value of size 8
==288950==    at 0x408D05: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950== 
==288950== Use of uninitialised value of size 8
==288950==    at 0x408D1C: _M_dispose (basic_string.h:239)
==288950==    by 0x408D1C: ~basic_string (basic_string.h:672)
==288950==    by 0x408D1C: ~MyClass2 (MyClass1.h:52)
==288950==    by 0x408D1C: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950== 
==288950== Invalid read of size 8
==288950==    at 0x408D1C: _M_dispose (basic_string.h:239)
==288950==    by 0x408D1C: ~basic_string (basic_string.h:672)
==288950==    by 0x408D1C: ~MyClass2 (MyClass1.h:52)
==288950==    by 0x408D1C: MyClass1::~MyClass1() (MyClass1.cpp:44)
==288950==    by 0x40281C: main (main.cpp:14)
==288950==  Address 0xa04da7678409ae8 is not stack'd, malloc'd or (recently) free'd
  
As before, we read the line numbers bottom up. Note these two lines that appear in the last two blocks:
==288950==    at 0x408D1C: _M_dispose (basic_string.h:239)
==288950==    by 0x408D1C: ~basic_string (basic_string.h:672)
  
basic_string.h is a built in C++ file (part of the implementation of string). In general, if we are using C++ provided functions or types, they will appear at the top of the line stack in valgrind output. We can assume that there is never anything wrong with C++ library code, so stick to looking at the lines of code corresponding to functions you wrote. In this example, the functions that should be checked are the destructors of MyClass1 and MyClass2. This particular code was compiled with the -g3 flag, hence we should look at the particular line numbers given above. Another useful thing to get from the last three blocks is that value which is causing problems is of size 8. While there are various C++ data types that take up 8 bytes, common ones that are 8 bytes and cause valgrind issues are the pointer types which are 8 bytes. The problem with this particular code is that we are trying to delete a pointer that was uninitialized. It is important to note that the delete is what occurs on MyClass1.cpp:44. That is, valgrind does not tell you where the variable is not initialized, but it does give a good hint in this case which particular variable is uninitialized.

Let's consider a third example of a memory error.
==2211302== Memcheck, a memory error detector
==2211302== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2211302== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==2211302== Command: ./a.out
==2211302== 
==2211302== Invalid read of size 8
==2211302==    at 0x409664: MyClass1::f1(std::__cxx11::basic_string, 
std::allocator >, MyClass4*) (MyClass1.cpp:97)
==2211302==    by 0x4092B3: MyClass1::f2(std::__cxx11::basic_string, 
std::allocator > const&, MyClass4*) (MyClass1.cpp:138)
==2211302==    by 0x4060DF: MyClass2::f3(std::__cxx11::basic_string,
std::allocator > const&, unsigned long, unsigned long) (MyClass2.cpp:180)
==2211302==    by 0x405D8A: MyClass2::f4(std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:162)
==2211302==    by 0x405757: MyClass2::f5(MyClass3*, std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:90)
==2211302==    by 0x40548E: MyClass2::f5(MyClass3*, std::__cxx11::basic_string,
std::allocator > const&) (MyClass2.cpp:83)
==2211302==    by 0x4050AC: MyClass2::f6(std::__cxx11::basic_string,
std::allocator > const&) (MyClass2.cpp:71)
==2211302==    by 0x404EC5: MyClass2::MyClass2(std::__cxx11::basic_string, 
std::allocator > const&, std::__cxx11::basic_string, std::allocator > 
const&, unsigned long, unsigned long, unsigned long, unsigned long) (MyClass2.cpp:52)
==2211302==    by 0x402966: main (main.cpp:10)
==2211302==  Address 0x188 is not stack'd, malloc'd or (recently) free'd
==2211302== 
==2211302== 
==2211302== Process terminating with default action of signal 11 (SIGSEGV)
==2211302==  Access not within mapped region at address 0x188
==2211302==    at 0x409664: MyClass1::f1(std::__cxx11::basic_string,
std::allocator >, MyClass4*) (MyClass1.cpp:97)
==2211302==    by 0x4092B3: MyClass1::f2(std::__cxx11::basic_string, 
std::allocator > const&, MyClass4*) (MyClass1.cpp:138)
==2211302==    by 0x4060DF: MyClass2::f3(std::__cxx11::basic_string, 
std::allocator > const&, unsigned long, unsigned long) (MyClass2.cpp:180)
==2211302==    by 0x405D8A: MyClass2::f4(std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:162)
==2211302==    by 0x405757: MyClass2::f5(MyClass3*, std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:90)
==2211302==    by 0x40548E: MyClass2::f5(MyClass3*, std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:83)
==2211302==    by 0x4050AC: MyClass2::f6(std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:71)
==2211302==    by 0x404EC5: MyClass2::MyClass2(std::__cxx11::basic_string, 
std::allocator > const&, std::__cxx11::basic_string, std::allocator > 
const&, unsigned long, unsigned long, unsigned long, unsigned long) (MyClass2.cpp:52)
==2211302==    by 0x402966: main (main.cpp:10)
==2211302==  If you believe this happened as a result of a stack
==2211302==  overflow in your program's main thread (unlikely but
==2211302==  possible), you can try to increase the size of the
==2211302==  main thread stack using the --main-stacksize= flag.
==2211302==  The main thread stack size used in this run was 8388608.
==2211302== 
==2211302== HEAP SUMMARY:
==2211302==     in use at exit: 83,072 bytes in 24 blocks
==2211302==   total heap usage: 69 allocs, 45 frees, 215,950 bytes allocated
==2211302== 
==2211302== 448 bytes in 1 blocks are definitely lost in loss record 19 of 22
==2211302==    at 0x4C38B6F: operator new[](unsigned long) (vg_replace_malloc.c:640)
==2211302==    by 0x408DC5: MyClass1 >::init(unsigned long, std::hash, std::allocator > >) (MyClass1.cpp:39)
==2211302==    by 0x404EB8: MyClass2::MyClass2(std::__cxx11::basic_string, 
std::allocator > const&, std::__cxx11::basic_string, std::allocator > 
const&, unsigned long, unsigned long, unsigned long, unsigned long) (MyClass2.cpp:51)
==2211302==    by 0x402966: main (main.cpp:10)
==2211302== 
==2211302== LEAK SUMMARY:
==2211302==    definitely lost: 448 bytes in 1 blocks
==2211302==    indirectly lost: 0 bytes in 0 blocks
==2211302==      possibly lost: 0 bytes in 0 blocks
==2211302==    still reachable: 82,624 bytes in 23 blocks
==2211302==         suppressed: 0 bytes in 0 blocks
==2211302== Reachable blocks (those to which a pointer was found) are not shown.
==2211302== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==2211302== 
==2211302== For lists of detected and suppressed errors, rerun with: -s
==2211302== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)    
  
In this output, we should focus on these two blocks:
==2211302== Invalid read of size 8
==2211302==    at 0x409664: MyClass1::f1(std::__cxx11::basic_string, 
std::allocator >, MyClass4*) (MyClass1.cpp:97)
==2211302==    by 0x4092B3: MyClass1::f2(std::__cxx11::basic_string, 
std::allocator > const&, MyClass4*) (MyClass1.cpp:138)
==2211302==    by 0x4060DF: MyClass2::f3(std::__cxx11::basic_string,
std::allocator > const&, unsigned long, unsigned long) (MyClass2.cpp:180)
==2211302==    by 0x405D8A: MyClass2::f4(std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:162)
==2211302==    by 0x405757: MyClass2::f5(MyClass3*, std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:90)
==2211302==    by 0x40548E: MyClass2::f5(MyClass3*, std::__cxx11::basic_string,
std::allocator > const&) (MyClass2.cpp:83)
==2211302==    by 0x4050AC: MyClass2::f6(std::__cxx11::basic_string,
std::allocator > const&) (MyClass2.cpp:71)
==2211302==    by 0x404EC5: MyClass2::MyClass2(std::__cxx11::basic_string, 
std::allocator > const&, std::__cxx11::basic_string, std::allocator > 
const&, unsigned long, unsigned long, unsigned long, unsigned long) (MyClass2.cpp:52)
==2211302==    by 0x402966: main (main.cpp:10)
==2211302==  Address 0x188 is not stack'd, malloc'd or (recently) free'd
==2211302== 
==2211302== 
==2211302== Process terminating with default action of signal 11 (SIGSEGV)
==2211302==  Access not within mapped region at address 0x188
==2211302==    at 0x409664: MyClass1::f1(std::__cxx11::basic_string,
std::allocator >, MyClass4*) (MyClass1.cpp:97)
==2211302==    by 0x4092B3: MyClass1::f2(std::__cxx11::basic_string, 
std::allocator > const&, MyClass4*) (MyClass1.cpp:138)
==2211302==    by 0x4060DF: MyClass2::f3(std::__cxx11::basic_string, 
std::allocator > const&, unsigned long, unsigned long) (MyClass2.cpp:180)
==2211302==    by 0x405D8A: MyClass2::f4(std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:162)
==2211302==    by 0x405757: MyClass2::f5(MyClass3*, std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:90)
==2211302==    by 0x40548E: MyClass2::f5(MyClass3*, std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:83)
==2211302==    by 0x4050AC: MyClass2::f6(std::__cxx11::basic_string, 
std::allocator > const&) (MyClass2.cpp:71)
==2211302==    by 0x404EC5: MyClass2::MyClass2(std::__cxx11::basic_string, 
std::allocator > const&, std::__cxx11::basic_string, std::allocator > 
const&, unsigned long, unsigned long, unsigned long, unsigned long) (MyClass2.cpp:52)  
Recall as from the previous examples, we read the line stacks in each block from bottom up. Here, we should specifically look at MyClass1.cpp:97 as it as at the top of the stack and is in a function (f1) in my class (MyClass1). A handy thing to know is that std::__cxx11::basic_string, std::allocator > const& is actually exactly string. The next two lines that are useful from this output besides the line number (MyClass1.cpp:97) are these:
Invalid read of size 8

Access not within mapped region at address 0x188
  
The first of these lines (and the fact that this is a valgrind error) tells us it's most likely an error with a pointer. The second line confirms this as we are trying to access (dereference) a pointer that has address 0x188 which is not in a mapped region. That is, it is a pointer to nothing. This indicates to us that we are most likely dereferencing a null pointer (which in C++ is defined as a pointer to nothing). Suppose we go to MyClass1.cpp:97 and find it to be rather complex, perhaps something like:
while (a[i].b != nullptr and not(a[i].c == c)) {
  
Which variable here is nullptr? You could discover this by checking if various variables are nullptr by printing. However, a quick way to tell is that there is actually only one variable that is being dereferenced in this line. In particular, a. Recall that a[i] is actually short hand for dereferencing the pointer i steps up from a (i.e. *(a+i)). To fix this issue, we should make sure a is not nullptr before using it.

One last thing to note about this output is the heap and leak summaries at the end. These seem to indicate more catastrophic errors due to the large numbers of allocs without frees and bytes allocated. However, it is important to keep in mind that this program crashed in the middle of running valgrind> so memory that was allocated properly never had a chance to be freed. This is important to keep in mind when fixing errors.

Summary

In short, valgrind is a powerful tool at your disposal to help you find errors. Learn how to use it and it will help you become a better programmer. Just a word of caution, though: the fact that you run valgrind and get no errors is not proof that your program is bug free. It is a very good step in that direction, though.

Another thing to note is that the valgrind output can be somewhat overwhelming. Consider the second memory error example above for instance. This code has only 1 incorrect line (not initializing a pointer to nullptr) but many lines of valgrind output. . Therefore, when you run unit_test on your code with valgrind enabled, we recommend that you comment out all but one of your failing tests. That way, the valgrind output will be more readable (and useful). Furthermore, this gives additional motivation to test your code gradually and in small chunks as much as possible.