Memory management
The ability to dynamically allocate and deallocate memory is one of the strongest features of C++ programming, but the greatest strength can also be the greatest weakness. This is certainly true of C++ applications, where memory-handling problems are among the most common bugs.
One of the most subtle and hard-to-detect bugs is the memory leak the failure to properly deallocate memory that was previously allocated. A small memory leak that occurs only once may not be noticed, but programs that leak large amounts of memory, or leak progressively, may display symptoms ranging from poor (and gradually decreasing) performance to running out of memory completely. Worse, a leaking program may use up so much memory that it causes another program to fail, leaving the user with no clue to where the problem truly lies. In addition, even harmless memory leaks may be symptomatic of other problems.
Valgrind
Valgrind is an instrumentation framework for building dynamic analysis tools. We will be focusing on the Memcheck tool in their extensive tool suite.
Note: Valgrind is designed for Linux, but it is also compatible with some versions of Mac OS (unless you are running a very new version of the OS). If you are performing this lab on Windows, it is recommended that you use
hammerto run valgrind.
Memcheck
Memcheck detects memory-management problems, and is aimed primarily at C and C++ programs. When a program is run under Memchecks supervision, all reads and writes of memory are checked, and calls to malloc/new/free/delete are intercepted. As a result, Memcheck can detect if your program:
- Accesses memory it shouldnt (areas not yet allocated, areas that have been freed, areas past the end of heap blocks, inaccessible areas of the stack).
- Uses uninitialized values in dangerous ways.
- Leaks memory.
- Does bad frees of heap blocks (double frees, mismatched frees).
- Passes overlapping source and destination memory blocks to
memcpy()and related functions.
Memcheck reports these errors as soon as they occur, giving the source line number at which it occurred, and also a stack trace of the functions called to reach that line. Memcheck tracks valid addresses and initialization of values. As a result, it can detect the use of uninitialized memory. Memcheck runs programs about 10-30x slower than normal.
Perparing your program
Compile your program with the following flags:
-gto include debugging information so that Memchecks error messages include exact line numbers.-O0to turn off all optimizations. Memchecks error messages can be slightly inaccurate at-O1and can lead to spurious errors at-O2or above.
For this lab the command:
will be sufficient.
Running your program under Memcheck
The examples/exercises in this lab dont use command line arguments, but if your program is called like this:
Then run Valgrind like this:
Memcheck is the default tool so no tool flags are necessary. The --leak-check option turns on the detailed memory leak detector.
Your program will run much slower (e.g. 20 30 times) than normal and use a lot more memory. Memcheck will issue messages about memory errors and leaks that it detects.
Examples
Lets go over some simple examples of the types of errors you can uncover using Valgrind and how to interpret the error messages. Were going to start with a simple c++ program (save the file as simple.cpp):
This is a pretty typical introduction to pointers example from CS012. It also contains a very common memory management mistake. On line 2 we allocated a contiguous location in memory large enough for 10 ints and assigned the starting address to an int pointer p. The next line then attempts to assign to the element with index 10. This may seem fine since it seems to be the last element in the array; however, due to 0-based addressing, the last element is actual p[9].
Now, lets compile the program:
And run it:
You may get results similar to:
Or it might even run, either way figuring out your error is cryptic and difficult. Lets run it through Valgrind (you dont need to recompile):
And youll get a much more in detailed report of what is going on:
Lets look at this bit by bit.
- 13621 is the process ID; typically it is not important.
- The rest shows some copyright and version information as well as the command Valgrind is running (your executable).
Now we get into the errors:
This shows us that the error was an Invalid write of size 4 on line (simple.cpp:3) in function main. The address of the line (0x108698) as well as the address of the memory (0x5b7dca8) are shown here and they can be incredibly useful when debugging some really nasty memory bugs on the stack/heap, but they arent helpful in this simple case. The rest of the error message will provide additional information that may help in identifying and fixing the error, in this case, a part of the root cause of the error comes from line (simple.cpp:2) with the operator new[](unsigned long).
After this we get a HEAP SUMMARY:
This shows us the second error we have. We can see here that we have 40 bytes in 1 blocks still in use at exit. Additionally, we had 2 allocs but only 1 free. There is memmory that was allocated but then did not have a corresponding deallocation. Looking at the records (second half of the message), we can see that 40 bytes in 1 block are definitely lost in loss record 1 of 1. We are given some additional information to help track it down. operator new[](unsigned long) at main (simple.cpp:2) is at fault here.
Finally, we are given a summary:
We have 40 bytes in 1 blocks definitely lost and nothing indirectly, or possibly lost, no memory still reachable and no errors were suppressed. Dont worry, the next examples will cover these types of errors as well. You can add to verbosity by adding additional -v flags, but the default typically has enough information to cover most errors.
Lets fix the errors one at a time. First, lets get rid of the Invalid write since that is causing our program to crash. The fix is quite simple, figure out the size of the array, the last index in the array, and make sure you are in bounds of the array.
And compile and run again (I add a trick I frequently use, the && means execute the second command only if the first command succeeds):
Now your program should run to completion (you wont see any output). Woohoo! You fixed itwait, there were two errors werent there? Lets run it back through Valgrind and see:
And youll see the following (tool information excluded for brevity):
We can clearly see from our summaries that we are still leaking memory even though we dont have anymore write errors. We have the same error as above, but to remind us, we have 40 bytes in 1 blocks definitely lost in record 1 due to the operator new[](unsigned long) on line (simple.cpp:2) in main function. Lets look at that code block (between { and }):
The new operator allocates new memory, but we never deallocate that memory. Lets do that now:
Yes I used the wrong delete but this was done on purpose, bear with me. If you didnt realize that was the wrong delete, thats exactly what this tool helps with so dont feel bad!
When a block is freed with an inappropriate deallocation function
- If allocated with
malloc,calloc,realloc,valloc, ormemalign, you must deallocate with free*. - If allocated with
new[], you must deallocate withdelete[]. - If allocated with
new, you must deallocate withdelete.
* These are common in C but less commonly seen in C++, so you likely havent seen them much at this point.
Lets run this through Valgrind again:
If we look at the summary it appears we freed all of the memory, however, if we look at the error list we will see that we used a Mismatched free() / delete / delete [] at operator delete(void*) on line (simple.cpp:5) in the main function. And if we look at the additional information we can see the root is from operator new[](unsigned long) at line (simple.cpp:2) in the main function. Looking at these two operators as shown by Valgrind, we can see that we allocated with a [] but didnt deallocate with the same. Lets fix that now.
And run Valgrind again:
Now were at what is called Memcheck-clean, we have 0 bytes in 0 blocks in use at exit, All heap blocks were freed -- no leaks are possible and we have 0 errors from 0 contexts. This is the goal of any program we write.
The final step, now that were at Memcheck-clean, is to re-run with optimizations turned back on:
Congratulations! Youve successfully cleaned up a simple program from all memory leaks using the Memcheck tool in the Valgrind suite.
Illegal read/illegal write errors
The first example showed an example of an Illegal write error. If we had tried to print out the value
We would have had a reported Invalid read of size 4 at that line. The rest of that error message looks very similar to the Invalide write error.
Use of uninitialized values
As a general rule, you should always initialize your values. Nonetheless there are times that it happens (unintentional or not). Memcheck will catch these errors with a caveat. Lets look at the following program (uninitialized.cpp):
Yes, it is again a silly examply, but it is used for illustrative purposes so bear with me. Lets compile and run this program:
This should run and will print a number (often 0), but I want to emphasize that this should not be expected and is not because of the C++ standard. x is unintialized, and you may very well get a junk value. (I have graded a lot of assignments where students submitted programs that worked for them because their uninitialized variables happened to be zero. When I graded them on my computer, they held junk values, and their programs failed. The only way to be sure is to test your code under valgrind.) Now, lets run it through Valgrind and see what happens:
Oh boythats a lot of errors, and they really dont seem friendly to look at. This is similar to compiler errors generated when using the Standard Template Library (STL), cout in this case. Lets take a look at the first error though:
Okayso there is a Conditional jump or move somewhere that depends on an unitialised value(s). You may have learned about jumps in CS061 if youve taken that course so far but the important part here is that it depends on an unitialised value(s). In fact, if we look at the second error we can see:
There it is, right at the top: Use of unintialised value of size 8. However, if we inspect the error message we see that it is at ??? by std::ostreambuf... by std::ostream&... by main (uninitialized.cpp:6). That last bybyby sequence is the stack trace. Its frequently a good idea to skip past all the library files and see what is the last function you wrote that caused that. We can see that in the main function on line 6 we set off this error. But we still dont know what caused it. We can see by inspecting line 6 cout << x << endl; that it is likely caused by the variable x, but thats about all we get. It turns out, we can add additional flags into the Valgrind call to get more out of this, specifically the --track-origins=yes flag:
Now lets look at that same error again (the second one):
Ive truncated the errors from the STL for brevity. You shouldnt need to dig into those in most errors. If you do, grab a cup of tea, clear your schedule for the week and get busy. Now we can see that the new flag added some additional information:
The variable at fault here was created by stack allocation in the main function from (unitialized.cpp:4). After some inspection of that function we can see that the variable x is at fault and can fix it by simply initializing it to some value, lets say 10.
Now, compiling and running Valgrind again we can see that we achieved Memcheck-clean.
It is important to note here that Valgrind will let your program copy around junk data as much as it likes. Memcheck will observe and keep track of the data, but doesnt complain. It doesnt complain until the use of uninitialized data might affect your programs externally-visible behaviour. Experiment with the following program to see how the generated reports look:
Another interesting example is this one:
When I run this example, the valgrind report flags line 5 (the std::cout line), line 14 (the call to foo) in the stack trace. It points to line 9 (the open brace on the main function) as the origin. The origin is actually line 10, where the x is declared. Note that the test for uninitialized values occurs when the value is used in a conditional, not on arithematic. Thus, valgrind will not flag the computation of y. A handy debugging trick is to insert dummy conditions to trigger the test, allowing you to test variables one by one to track down the problem.
Illegal frees
Memcheck will also track the memory that has been deallocated so if you try to re-deallocate memory (as in a double free) it will catch that and report it to you. Consider the following program (doubleFree.cpp):
By inspection it is easy to see that p has been deleted twice, but lets run it through Valgrind anyways:
The first (and only) error shows us that we have an Invalid free() / delete / delete[] / realloc() at operator delete(void*) on line (doubleFree.cpp:5) in the main function. This memory was free'd by operator delete(void*) on line (doubleFree.cpp:4) in the main function. The block was also originally alloc'd at operator new(unsigned long) on line doubleFree.cpp:2 in the main function. We can get to Memcheck-clean by simply removing either of the delete p; statements.
Memory leak detection
Memcheck has the following four leak kinds:
- Still reachable A start-pointer or chain of start-pointers to the block is found. The program could have in theory deallocated the memory.
- Definitely lost No pointer to the block can be found. There is no possible way to have deallocated this memory before the program exited.
- Indirectly lost The block is lost, not because there are no pointers to it, but rather because all the blocks that point to it are themselves lost. For example, if you have a binary tree and the root node is lost, all its children are indirectly lost. If you fixed the definitely lost block correctly these will be fixed as a side effect.
- Possibly lost A chain of one or more pointers to the block have been found, but at least one of them is an interior pointer. This is typically not good unless, through inspection, you identify the interior pointer and now how to access this memory.
A start-pointer is a pointer at the beginning of a block whereas an interior-pointer points somewhere in the middle of the block (intentionally or unintentionally), for example:
ppoints to the beginning of the array and is a start-pointerippoints to the middle of the array and is an interior-pointer
There are many more kinds of interior pointers and you can read more about them on your own.
Lab exercise
Now use what you have learned from this lab to get the lineage program to Memheck-clean. The program is a simplified family tree manager. It maintains a list of Persons in a PersonList. Each Person object maintains a set of pointers to his/her parents as well as to his/her children. The code contains several memory leaks. To compile:
* The -fno-inline instructs the compiler to not inline any functions and makes it easier to see the function call chain.
Some things to consider:
- As with compiler errors, its a good idea to start at the top and see if fixing those errors fix later errors.
- Sometimes by fixing an error you introduce more. These arent new errors, they were just revealed by fixing an earlier error.
- Think carefully about what
deletevs.delete[]do and why we spent time in CS014 discussing shallow vs. deep copies. - If you get to only (or mostly) indirectly lost errors youll notice that Valgrind doesnt report each one individually. This is because they arent lost so much as forgotten by the programmer. If this happens you can turn on
--show-reachable=yesto show more details about each of those errors.
5/5 – (1 vote)
$ g++ -g -O0 *.cpp -o <exec_name>
$ ./myprog arg1 arg2
$ valgrind --leak-check=full ./myprog arg1 arg2
int main() { int *p = new int[10]; p[10] = 1; return 0;}
$ g++ -g -O0 -o example1 simple.cpp
$ ./example1
example1: malloc.c:2401: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.Aborted (core dumped)
$ valgrind --leak-check=full ./example1
==13621== Memcheck, a memory error detector==13621== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.==13621== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info==13621== Command: ./example1==13621== ==13621== Invalid write of size 4==13621== at 0x108698: main (simple.cpp:3)==13621== Address 0x5b7dca8 is 0 bytes after a block of size 40 alloc'd==13621== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==13621== by 0x10868B: main (simple.cpp:2)==13621== ==13621== ==13621== HEAP SUMMARY:==13621== in use at exit: 40 bytes in 1 blocks==13621== total heap usage: 2 allocs, 1 frees, 72,744 bytes allocated==13621== ==13621== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1==13621== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==13621== by 0x10868B: main (simple.cpp:2)==13621== ==13621== LEAK SUMMARY:==13621== definitely lost: 40 bytes in 1 blocks==13621== indirectly lost: 0 bytes in 0 blocks==13621== possibly lost: 0 bytes in 0 blocks==13621== still reachable: 0 bytes in 0 blocks==13621== suppressed: 0 bytes in 0 blocks==13621== ==13621== For counts of detected and suppressed errors, rerun with: -v==13621== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==13621== Memcheck, a memory error detector==13621== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.==13621== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info==13621== Command: ./example1
==13621== Invalid write of size 4==13621== at 0x108698: main (simple.cpp:3)==13621== Address 0x5b7dca8 is 0 bytes after a block of size 40 alloc'd==13621== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==13621== by 0x10868B: main (simple.cpp:2)
==13621== HEAP SUMMARY:==13621== in use at exit: 40 bytes in 1 blocks==13621== total heap usage: 2 allocs, 1 frees, 72,744 bytes allocated==13621== ==13621== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1==13621== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==13621== by 0x10868B: main (simple.cpp:2)
==13621== LEAK SUMMARY:==13621== definitely lost: 40 bytes in 1 blocks==13621== indirectly lost: 0 bytes in 0 blocks==13621== possibly lost: 0 bytes in 0 blocks==13621== still reachable: 0 bytes in 0 blocks==13621== suppressed: 0 bytes in 0 blocks==13621== ==13621== For counts of detected and suppressed errors, rerun with: -v==13621== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
int main() { int *p = new int[10]; p[9] = 1; // Correct this to size - 1 = 10 - 1 = 9 return 0;}
$ g++ -g -O0 simple.cpp -o example1 && ./example1
$ valgrind ./example1
==14490== HEAP SUMMARY:==14490== in use at exit: 40 bytes in 1 blocks==14490== total heap usage: 2 allocs, 1 frees, 72,744 bytes allocated==14490== ==14490== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1==14490== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==14490== by 0x10868B: main (simple.cpp:2)==14490== ==14490== LEAK SUMMARY:==14490== definitely lost: 40 bytes in 1 blocks==14490== indirectly lost: 0 bytes in 0 blocks==14490== possibly lost: 0 bytes in 0 blocks==14490== still reachable: 0 bytes in 0 blocks==14490== suppressed: 0 bytes in 0 blocks==14490== ==14490== For counts of detected and suppressed errors, rerun with: -v==14490== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
{ int *p = new int[10]; p[9] = 1; return 0;}
{ int *p = new int[10]; p[9] = 1; delete p; // Deallocate the memory from the new above return 0;}
==14593== Mismatched free() / delete / delete []==14593== at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==14593== by 0x10871E: main (simple.cpp:5)==14593== Address 0x5b7dc80 is 0 bytes inside a block of size 40 alloc'd==14593== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==14593== by 0x1086FB: main (simple.cpp:2)==14593== ==14593== ==14593== HEAP SUMMARY:==14593== in use at exit: 0 bytes in 0 blocks==14593== total heap usage: 2 allocs, 2 frees, 72,744 bytes allocated==14593== ==14593== All heap blocks were freed -- no leaks are possible==14593== ==14593== For counts of detected and suppressed errors, rerun with: -v==14593== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
{ int *p = new int[10]; p[9] = 1; delete[] p; // Deallocate the memory from the new above return 0;}
==14620== HEAP SUMMARY:==14620== in use at exit: 0 bytes in 0 blocks==14620== total heap usage: 2 allocs, 2 frees, 72,744 bytes allocated==14620== ==14620== All heap blocks were freed -- no leaks are possible==14620== ==14620== For counts of detected and suppressed errors, rerun with: -v==14620== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$ g++ -g -O2 *.cpp -o example1
cout << p[10] << endl;
#include <iostream>using namespace std;int main() { int x; cout << x << endl; return 0;}
$ g++ -g -O0 uninitialized.cpp -o example2 && ./example2
==14854== Conditional jump or move depends on uninitialised value(s)==14854== at 0x4F43B2A: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F50074: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x1088A2: main (uninitialized.cpp:6)==14854== ==14854== Use of uninitialised value of size 8==14854== at 0x4F4362E: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F43B53: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F50074: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x1088A2: main (uninitialized.cpp:6)==14854== ==14854== Conditional jump or move depends on uninitialised value(s)==14854== at 0x4F4363B: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F43B53: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F50074: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x1088A2: main (uninitialized.cpp:6)==14854== ==14854== Conditional jump or move depends on uninitialised value(s)==14854== at 0x4F43B86: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F50074: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x1088A2: main (uninitialized.cpp:6)==14854== 0==14854== ==14854== HEAP SUMMARY:==14854== in use at exit: 0 bytes in 0 blocks==14854== total heap usage: 2 allocs, 2 frees, 73,728 bytes allocated==14854== ==14854== All heap blocks were freed -- no leaks are possible==14854== ==14854== For counts of detected and suppressed errors, rerun with: -v==14854== Use --track-origins=yes to see where uninitialised values come from==14854== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)
==14854== Conditional jump or move depends on uninitialised value(s)==14854== at 0x4F43B2A: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F50074: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x1088A2: main (uninitialized.cpp:6)
==14854== Use of uninitialised value of size 8==14854== at 0x4F4362E: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F43B53: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x4F50074: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)==14854== by 0x1088A2: main (uninitialized.cpp:6)
$ valgrind --leak-check=full --track-origins=yes ./example2
==14904== Use of uninitialised value of size 8==14904== ...==14904== ...==14904== ...==14904== by 0x1088A2: main (uninitialized.cpp:6)==14904== Uninitialised value was created by a stack allocation==14904== at 0x10888A: main (uninitialized.cpp:4)
==14904== Uninitialised value was created by a stack allocation==14904== at 0x10888A: main (uninitialized.cpp:4)
int x = 10;
==14973== HEAP SUMMARY:==14973== in use at exit: 0 bytes in 0 blocks==14973== total heap usage: 2 allocs, 2 frees, 73,728 bytes allocated==14973== ==14973== All heap blocks were freed -- no leaks are possible==14973== ==14973== For counts of detected and suppressed errors, rerun with: -v==14973== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
#include <iostream>using namespace std;int main() { int x; bool z; int y = x + 5; if (x) { cout << "X is non-zero" << endl; } if (z) { cout << "Z is truthy" << endl; } cout << y << endl; return 0;}
#include <iostream>void foo(int x){ std::cout<<x<<std::endl;}int main(){ int x; int r=4; int y=x+r; foo(y); return 0;}
#include <iostream>void foo(int x){ std::cout<<x<<std::endl;}int zzzz=0; // dummy variableint main(){ int x; int r=4; int y=x+r; if(x) zzzz++; // uninitialized error reported here if(r) zzzz++; if(y) zzzz++; // uninitialized error reported here foo(y); return 0;}
int main() { int *p = new int(10); delete p; delete p; return 0;}
==15125== Invalid free() / delete / delete[] / realloc()==15125== at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==15125== by 0x108727: main (doubleFree.cpp:5)==15125== Address 0x5b7dc80 is 0 bytes inside a block of size 4 free'd==15125== at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==15125== by 0x108716: main (doubleFree.cpp:4)==15125== Block was alloc'd at==15125== at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==15125== by 0x1086FB: main (doubleFree.cpp:2)==15125== ==15125== ==15125== HEAP SUMMARY:==15125== in use at exit: 0 bytes in 0 blocks==15125== total heap usage: 2 allocs, 3 frees, 72,708 bytes allocated==15125== ==15125== All heap blocks were freed -- no leaks are possible==15125== ==15125== For counts of detected and suppressed errors, rerun with: -v==15125== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
int *p = new int[10]int *ip = p + 2;

![[Solved] CS100 Lab-04 valgrind](https://assignmentchef.com/wp-content/uploads/2022/08/downloadzip.jpg)

![[Solved] CS100 Lab7-Factory Pattern](https://assignmentchef.com/wp-content/uploads/2022/08/downloadzip-1200x1200.jpg)
Reviews
There are no reviews yet.