diff --git a/labs/list_iterators/README.md b/labs/list_iterators/README.md new file mode 100644 index 0000000..a8054e9 --- /dev/null +++ b/labs/list_iterators/README.md @@ -0,0 +1,101 @@ +# Lab 6 — Reversing Data 8 Ways: STL Vectors vs. STL Lists vs. Homemade Linked Lists + +## Checkpoint 1A: Reverse with STL Vector Swaps +*estimate: TBD* + +Read the starter code [checkpoint1.cpp](checkpoint1.cpp) and complete the function *reverse* that reverses the contents of an STL vector of integers. For example, if the contents of the vector are in increasing order before the call to reverse_vector, then they will be in decreasing order afterwards. For this checkpoint, use indexing/subscripting/[] on the vector, not iterators (or pointers). You may not use a second vector or array or list. +The trick is to step through the vector one location at a time, swapping values between the first half of the +vector and the second half. As examples, the value at location 0 and the value at location size()-1 must +be swapped, and the value at location 1 and the value at location size()-2 must be swapped. +Make sure your code works with even and odd length vectors. Also add a few more tests to the main function +to make sure your code will work for the special cases of an empty vector and vectors of size 1 and 2. + +## Checkpoint 1B: Reverse with STL List Swaps +*estimate: TBD* + +Copy your code from Checkpoint 1A to a new file. Then, convert this code to use STL Lists instead of STL +vectors. Start by replacing ’vector’ with ’list’ everywhere. And you’ll need to replace your subscripting +with iterators. + +You may want to use a straightforward concept we did not discuss in lecture: a reverse iterator. A reverse +iterator is designed to step through a list from the back to the front. An example will make the main +properties clear: + +```cpp +std::list a; +unsigned int i; +for ( i=1; i<10; ++i ){ + a.push_back( i*i ); +} +std::list::reverse_iterator ri; +for( ri = a.rbegin(); ri != a.rend(); ++ri ){ + std::cout << *ri << std::endl; +} +``` + +This code will print out the values 81, 64, 49, . . . , 1, in order, on separate lines. You can also compile and run this [example program](reverse_iterator.cpp). + +Observe the type for the reverse iterator, the use of the functions rbegin and rend to provide iterators that delimit the bounds on +the reverse iterator, and the use of the ++ operator to take one step backwards through the list. It is very +important to realize that rbegin and end are NOT the same thing! One of the challenges here will be +determining when to stop (when you’ve reached the halfway point in the list). You may use an integer +counter variable to help you do this. + +For this checkpoint you should not use *erase*, or *insert*, or the *push* or *pop* functions. +Note, you’ll probably need to add the keyword typename in front of your templated iterator types to unconfuse the compiler. + +```cpp +typename std::list::iterator itr = data.begin(); +``` + +**To complete this checkpoint**, show a TA your debugged functions to reverse STL vectors and STL lists +by element swapping. Be sure to ask your TA/mentors any questions you have about regular vs. reverse +iterators for lists and vectors. + +## Checkpoint 2: Reverse with STL List Using Insert/Erase/Push/Pop +*estimate: TBD* + +Form a team of 4. You may form a team of 2 or 3 only with approval from your graduate lab TA. + +Each student should make a copy of their solution file for Checkpoint 1B. And then, each student should +rewrite their STL list reverse function: + - **STUDENT 1** Using only front(), pop_front(), and insert(). + - **STUDENT 2** Using only back(), pop_back(), insert(), and iterator increment. + - **STUDENT 3** Using only erase(), push_front(), and iterator dereference. + - **STUDENT 4** Using only erase(), push_back(), iterator dereference, and iterator decrement. +Each of these solutions is allowed to use a for or while loop and a temporary variable to store a single +element, but should not use any auxiliary data structures (no array, no vector, no additional list, etc.) + +Note that these solutions are quite different than the algorithms that reverse a vector or list by swapping +values. If the algorithm is paused and printed midway, the vector contents will be different than the algorithms +for Checkpoint 1. (Go ahead, add more calls to the print function in the middle of your reverse function.) Test +and debug your own code before helping your teammates. Discuss the similarities and differences between +the solutions to each version of the reverse function. + +**To complete this checkpoint**, as a team, present your debugged solutions to a TA or mentor. + +## Checkpoint 3: Reversing a Homemade Linked List +*estimate: TBD* + +This checkpoint is an individual checkpoint. (But you can ask your teammates questions if you get stuck.) +Pull out some paper. Following the conventions from lecture, draw a picture of a “homemade” singly-linked +list that stores the values 1, 2, 3, and 4. Make a variable on the stack named my_list of type Node* that +points to the first node in the chain (the node storing the value 1). The 4 node objects should be separate +blobs of memory dynamically-allocated on the heap. + +Now, modify this diagram to reverse the list – you can do this by only changing pointers! You should use the +existing node objects. Don’t copy the entire diagram or make any new nodes. You should not change the +values inside of any node – don’t swap values like we did for Checkpoint 1. +Then, write pseudo-code to reverse this list, just changing the pointers as you diagrammed above. You may +use helper variables (of type Node*) but no other data structures or variables. Remember that when we +directly manipulate homemade linked lists we don’t use iterators. + +Finally, read the starter code of checkpoint 3: [checkpoint3.cpp](checkpoint3.cpp). Complete the reverse function using your diagram and pseudocode as a guide. Test and debug the code. Add a few additional test cases to the main function to ensure your code works with an empty list, +and lists with one or two values. Also add a test or two of a node chain with something other than ints. +If you have time, write 2 versions of this function, one version should be iterative (using a for or while loop) +and one version should be recursive. + +**Note**: this reverse function takes a pointer as its argument, but we are passing this pointer by reference, because we want to modify this pointer. To understand the concept of passing a pointer by reference, you are recommended to read and run this [example program](reference_to_a_pointer.cpp). + +**To complete this checkpoint**, show a TA or mentor your diagram and your debugged function(s) to +reverse a homemade singly-linked list. diff --git a/labs/list_iterators/checkpoint1.cpp b/labs/list_iterators/checkpoint1.cpp new file mode 100644 index 0000000..e7b9ea4 --- /dev/null +++ b/labs/list_iterators/checkpoint1.cpp @@ -0,0 +1,47 @@ +#include +#include +#include + + +template +void print(std::vector &data, const std::string &label) { + std::cout << label << " "; + for (int i = 0; i < data.size(); i++) { + std::cout << " " << data[i]; + } + std::cout << std::endl; +} + + +template +void reverse(std::vector &data) { + + // FILL IN THIS FUNCTION + +} + + +int main() { + std::vector data; + data.push_back(1); + data.push_back(2); + data.push_back(3); + data.push_back(4); + data.push_back(5); + data.push_back(6); + data.push_back(7); + + print(data,"before:"); + reverse(data); + print(data,"after: "); + + std::vector data2; + data2.push_back("apple"); + data2.push_back("banana"); + data2.push_back("carrot"); + data2.push_back("date"); + + print(data2,"before:"); + reverse(data2); + print(data2,"after: "); +} diff --git a/labs/list_iterators/checkpoint3.cpp b/labs/list_iterators/checkpoint3.cpp new file mode 100644 index 0000000..95ed1bd --- /dev/null +++ b/labs/list_iterators/checkpoint3.cpp @@ -0,0 +1,63 @@ +#include +#include + + +// Note: It's ok that all the member variables are public for this +// tiny class. +template +class Node { +public: + T value; + Node *ptr; +}; + + + +template +void print(Node *data, const std::string &label) { + std::cout << label; + Node *tmp = data; + while (tmp != NULL) { + std::cout << " " << tmp->value; + tmp = tmp->ptr; + } + std::cout << std::endl; +} + + + +template +void reverse(Node* &input) { + + // FILL IN THIS FUNCTION + +} + + + + +int main() { + + // manually create a linked list of nodes with 4 elements + Node* my_list = new Node; + my_list->value = 1; + my_list->ptr = new Node; + my_list->ptr->value = 2; + my_list->ptr->ptr = new Node; + my_list->ptr->ptr->value = 3; + my_list->ptr->ptr->ptr = new Node; + my_list->ptr->ptr->ptr->value = 4; + my_list->ptr->ptr->ptr->ptr = NULL; + + + print(my_list,"my_list before"); + reverse(my_list); + print(my_list,"my_list after "); + + + // Note: We are not deleting any of the Nodes we created... so this + // program has memory leaks! + +} + +// =========================================================================== diff --git a/labs/list_iterators/reference_to_a_pointer.cpp b/labs/list_iterators/reference_to_a_pointer.cpp new file mode 100644 index 0000000..f7682e7 --- /dev/null +++ b/labs/list_iterators/reference_to_a_pointer.cpp @@ -0,0 +1,28 @@ +/* This example demonstrates the usage of passing a pointer by reference. + * It is needed when you want to modify the pointer. + */ + +#include + +// function to modify the value of a pointer through reference +void modifyPointer(int* & ptr, int& newValue) { + ptr = &newValue; // assign the address of newValue to the pointer +} + +int main() { + int value = 42; + int* ptr = &value; + + // print the original value of the pointer + std::cout << "Original value of pointer: " << *ptr << std::endl; + + int newValue = 100; // new value to assign to the pointer + // pass the pointer by reference to the function, so that we can change the pointer + modifyPointer(ptr, newValue); + + // print the modified value of the pointer + std::cout << "Modified value of pointer: " << *ptr << std::endl; + + return 0; +} + diff --git a/labs/list_iterators/reverse_iterator.cpp b/labs/list_iterators/reverse_iterator.cpp new file mode 100644 index 0000000..2a73cd6 --- /dev/null +++ b/labs/list_iterators/reverse_iterator.cpp @@ -0,0 +1,18 @@ +#include +#include + +int main(){ + + std::list a; + unsigned int i; + for ( i=1; i<10; ++i ){ + a.push_back( i*i ); + } + + std::list::reverse_iterator ri; + for( ri = a.rbegin(); ri != a.rend(); ++ri ){ + std::cout << *ri << std::endl; + } + + return 0; +} diff --git a/labs/memory_debugging/README.md b/labs/memory_debugging/README.md new file mode 100644 index 0000000..80d22af --- /dev/null +++ b/labs/memory_debugging/README.md @@ -0,0 +1,128 @@ +# Lab 4 — Memory Diagrams and Memory Debugging + +## Checkpoint 1 +*estimate: 30-45 minutes* + +- Form a group of 2 members. If the number of students in the room is not divisible by 2, the graduate TA will approve a single team with 3 members. + +- Introduce yourself to your teammate. Share to your teammate something about yourself (e.g. hobbies, sports, favorite music, etc). Learn something new about your teammate (even if you already know the teammate). + +- Our TA/mentor will pick 3 code snippets for each student, from the following 5 code snippets. Following the conventions used in Data Structures lecture for memory diagramming, draw a picture of the stack and the heap that result from executing the code snippet. Use a ‘?’ to represent uninitialized values. + +[Code Snippet 1](fruits.cpp) +[Code Snippet 2](grains.cpp) +[Code Snippet 3](desserts.cpp) +[Code Snippet 4](veggies.cpp) +[Code Snippet 5](protein.cpp) + +Show your diagram to your teammate, and ask the teammate to reverse engineer code for your diagram; at the same time, you reverse engineer code for your teammate's diagram. + +When everyone has finished writing code for the drawing, discuss your answers as a group. Are differences in the original and reverse engineered code valid alternate answers? Or was something misinterpreted? Critique each other’s diagrams. What could be improved or drawn more clearly? What statements need to be added to each example to clean up / prevent memory leaks? + +**To complete this checkpoint**, present your work to a TA/mentor. + +## Checkpoint 2 & 3: + +Checkpoints 2 and 3 focus on using a memory debugger. It is highly recommended that you thoroughly read the instructions for Checkpoint 2 and Checkpoint 3 before starting. + +Memory debuggers will be a useful tool in many of the assignments this semester, and in C++ development if you work with the language outside of this course. While a traditional debugger lets you step through your code and examine variables, a memory debugger instead reports memory-related errors during execution and can help find memory leaks after your program has terminated. The next time you see a “segmentation fault”, or it works on your machine but not on Submitty, try running a memory debugger! + +Read lecture notes [6.7](../../lectures/06_memory#67-memory-debugging), [6.8](../../lectures/06_memory#68-sample-buggy-program), [6.9](../../lectures/06_memory#69-using-dr-memory-httpwwwdrmemoryorg), [6.10](../../lectures/06_memory#610-using-valgrind-httpvalgrindorg), and [6.11](../../lectures/06_memory#610-using-valgrind-httpvalgrindorg), and try to understand the example buggy program code and what DrMemory or Valgrind say about this buggy program. Ask questions if you don't understand the code or if you don't understand the output of DrMemory or the output of Valgrind. + +Please download the following 4 files needed for this lab: + +[buggy_lab4.cpp](./buggy_lab4.cpp) +[first.txt](./first.txt) +[middle.txt](./middle.txt) +[last.txt](./last.txt) + +## Checkpoint 2 +*estimate: 20-40 minutes* + +For Checkpoint 2 of this lab, we will heavily rely on dynamic memory to find the average and smallest number for a set of data from an input file. You will use a memory debugging tool such as DrMemory or Valgrind to fix memory errors and leaks in buggy_lab4.cpp. Make sure to download the provided .txt files as well. +First take a look at the code inside the identifyMeanAndMin() function. You will notice that the syntax used +throughout the program may be a little different than what you are used to. Try and familiarize yourself with +this syntax before you start working on the assignment. What does this line of code do? + +```cpp +*(intArray + *numElements) = readInt; +``` + +Whats going on inside the *for* loop? If you are stuck on this, ask a mentor or TA for help as +soon as possible and refer back to your lecture notes on pointers and arrays. +Once you have done this, compile the program using: +```console +g++ buggy_lab4.cpp -o buggy_lab4.out -g -Wall +``` + +Try running the program normally using: + +```console +./buggy_lab4.out +``` + +You will notice that a segmentation fault occurs. Now run this program using either Valgrind or DrMemory. If running using Valgrind, remember to use --leak-check=yes or --leak-check=full. + +Your memory debugger should give you more context as to why a segmentation fault is occurring, especially if you have compiled with *-g*. To complete this checkpoint, add or modify code in the areas marked Parts 1, 2, and 3 to resolve all memory errors and leaks. As you are working on this, be sure to also think about the questions asked in Checkpoint 3. + +It is highly recommended that you tackle one part at a time. For example, after adding a few lines of code to part 1, you will now receive different memory errors when you recompile and run the program using your memory debugger. Similarly, fixing all memory errors in part 2 will generate different memory errors that should be resolved in part 3. + +In part 2 of buggy_lab4.cpp, the goal is to print out the contents of intArray in reverse order, while also calculating the sum of all elements in the array and keeping track of the smallest number encountered. Solutions that attempt to print the contents of the array in a different manner or end up with the wrong +value for the smallest number found or sum won't be accepted. +Note that: +- You are only allowed to modify or add code when asked to. This would be between the comments that +indicate parts 1, 2, and 3 inside buggy_lab4.cpp. Do not modify other parts of the code or create any +helper functions. +- You are not allowed to declare new variables; the ones provided are more than enough. Hint: how do +we create memory on the heap? +- You are not allowed to use additional data structures (like arrays or vectors). + +**You will receive no credit if you do not follow the above restrictions.** + +**To receive credit for this checkpoint**: Fix buggy_lab4.cpp so that it successfully prints out the average +and smallest number for a given set of data and is free of all memory errors and leaks on your local machine. +Submit buggy_lab4.cpp on Submitty and verify that there are no memory errors there as well and show a +mentor or TA both results. Also explain to a mentor TA what you added or modified in the program to +resolve all memory errors. + +## Checkpoint 3 +*estimate: 15-25 minutes* + +As you work through Checkpoint 2, try and pay close attention the analysis given by DrMemory or Valgrind +and think about the following: + +1. How does the output from your memory debugger differ when you compile your program with the -g +flag compared to when you leave it out? +2. How would you rewrite the for loop in part 2 to use the bracket [] operator instead of pointer syntax? +3. For DrMemory users, you would have encountered all of these errors in parts 1, 2, or 3 of Checkpoint 2: + +```console +UNITIALIZED READ +UNADDRESSABLE ACCESS +INVALID HEAP ARGUMENT +LEAK +``` + +4. What do you think each of these errors mean? +5. For Valgrind users, the errors you will have seen are: +```console +Use of uninitialised value +Invalid write +Invalid read +Conditional jump or move depends on uninitialised value(s) +Mismatched free() / delete / delete [] +? bytes in ? blocks are definitely lost in loss record ? of ? +``` + +**To receive credit for this checkpoint**, discuss your answers to these questions with a TA or mentor. + +## Checkpoint 4 +*remainder of the lab time* + +For this checkpoint, you team up with one or two other students from your lab section. Our TA or mentor will pick one problem from the practice packet for you two (or three) to work together as a group. You should not look at the posted solutions. Work as a group (e.g., pair programming!) to come up with a complete solution to the problem. + +Once you have completed and proofread and triple-checked your answer, launch your C++ development environment. First, focus on getting it to compile. What basic, minimal testing infrastructure is necessary to get your solution to compile? What small bits of syntax were you missing or incorrect or sloppy? Try to avoid making these mistakes on Thursday's test! + +Then, write a simple test case to exercise your solution. Compile and run the code. As time follows, fix bugs in your solution and improve and expand your test cases. + +**To complete this checkpoint**, When about 10 minutes remain in the lab, the TA and mentors will circulate through the room to finalize checkoffs. Show them your initial solution, and explain the typos and errors you found and debugged in working through your computer tested solution. Explain your testing & debugging process. diff --git a/labs/memory_debugging/buggy_lab4.cpp b/labs/memory_debugging/buggy_lab4.cpp new file mode 100644 index 0000000..302d8fb --- /dev/null +++ b/labs/memory_debugging/buggy_lab4.cpp @@ -0,0 +1,116 @@ +#include +#include + +#define MAX_ARRAY_SIZE 50 + + + +/** IMPORTANT: SOLUTIONS THAT DO NOT FOLLOW BELOW INSTRUCTIONS WILL RECEIVE NO CREDIT + * Do not add or modify code in this file UNLESS ASKED TO! + * You are NOT allowed to declare new variables; use the ones already declared. + * You are NOT allowed to create new helper functions or modify checkCorrectSmallest() +**/ + + + +/** DO NOT MODIFY THIS FUNCTION! **/ +int checkCorrectSmallest(int* smallestNum, const std::string& filename) { + if (filename == "first.txt" || filename == "middle.txt") { + if (*smallestNum != 1) {return -1;} + } + + else if (filename == "last.txt") { + if (*smallestNum != 22) {return -1;} + } + + return 1; +} + +/** A function that will identify the mean and smallest number + * in a set of data provided that there are at most 50 numbers + * in the file. + * + * @param filename: The name of a file that contains a list of numbers. + * + * Task: Add or modify code in the appropriate sections to fix memory and logic errors + * without using data structures (such as an array or vector) and without using any + * additional memory on the stack. +**/ + +void identifyMeanAndMin(const std::string& filename) { + int* numElements; + int* sum; + int* smallestNum; + float* avg; + + /** PART 1: ADD CODE BELOW **/ + + + + + /** PART 1: ADD CODE ABOVE **/ + + *numElements = 0; + *sum = 0; + + int readInt; + int* intArray = new int[MAX_ARRAY_SIZE]; + std::ifstream input(filename); + + while (input >> readInt) { + *(intArray + *numElements) = readInt; + *numElements += 1; + } + + std::cout << "Printing the contents of the array in reverse order: "; + + /** PART 2: MODIFY CODE BELOW **/ + + for (int i = MAX_ARRAY_SIZE; i >= -1; i--) { + // If we're at the beginning of the for loop, initalize *smallestNum + // Else, compare *smallestNum to current element in the for loop + if (i == MAX_ARRAY_SIZE) { + *smallestNum = *(intArray + i); + } + + else { + if (*smallestNum > *(intArray + i)) { + *smallestNum = *(intArray + i); + } + } + + /** PART 2: MODIFY CODE ABOVE **/ + + *sum += *(intArray + i); + std::cout << *(intArray + i) << " "; + } + + std::cout << std::endl; + if (checkCorrectSmallest(smallestNum, filename) == -1) { + std::cout << "ERROR: incorrect value for smallest number" << std::endl; + return; + } + + *avg = *sum / float(*numElements); + std::cout << "The average for the set of numbers in " << filename << " is " + << *avg << " and the smallest number is " << *smallestNum + << std::endl; + + /** PART 3: ADD AND/OR MODIFY CODE BELOW **/ + + delete intArray; + + /** PART 3: ADD AND/OR MODIFY CODE ABOVE **/ +} + +int main() { + identifyMeanAndMin("first.txt"); + std::cout << std::endl; + + identifyMeanAndMin("middle.txt"); + std::cout << std::endl; + + identifyMeanAndMin("last.txt"); + return 0; +} + diff --git a/labs/memory_debugging/desserts.cpp b/labs/memory_debugging/desserts.cpp new file mode 100644 index 0000000..7adc7a9 --- /dev/null +++ b/labs/memory_debugging/desserts.cpp @@ -0,0 +1,10 @@ +bool** cake; +bool pie; +bool fudge; +pie = true; +cake = new bool*[5]; +cake[1] = &pie; +bool* donut = new bool; +*donut = false; +cake[2] = donut; +cake[4] = &fudge; diff --git a/labs/memory_debugging/first.txt b/labs/memory_debugging/first.txt new file mode 100644 index 0000000..94ebaf9 --- /dev/null +++ b/labs/memory_debugging/first.txt @@ -0,0 +1,4 @@ +1 +2 +3 +4 diff --git a/labs/memory_debugging/fruits.cpp b/labs/memory_debugging/fruits.cpp new file mode 100644 index 0000000..be8a768 --- /dev/null +++ b/labs/memory_debugging/fruits.cpp @@ -0,0 +1,10 @@ +int pear = 3; +int* apple; +int banana[pear]; +int* orange; +apple = new int[pear]; +orange = &banana[1]; +apple[0] = 6; +apple[1] = 7; +apple[2] = 8; +*orange = 5; diff --git a/labs/memory_debugging/grains.cpp b/labs/memory_debugging/grains.cpp new file mode 100644 index 0000000..32dfed6 --- /dev/null +++ b/labs/memory_debugging/grains.cpp @@ -0,0 +1,10 @@ +float* oat[3]; +oat[1] = new float; +*oat[1] = 3.14; +oat[2] = new float; +*oat[2] = 6.02; +float rice; +float* wheat; +wheat = oat[2]; +float** barley = new float*; +*barley = oat[1]; diff --git a/labs/memory_debugging/last.txt b/labs/memory_debugging/last.txt new file mode 100644 index 0000000..e0b0db9 --- /dev/null +++ b/labs/memory_debugging/last.txt @@ -0,0 +1,49 @@ +22 +247 +279 +240 +253 +342 +87 +181 +391 +57 +23 +302 +168 +367 +236 +240 +187 +368 +216 +185 +31 +255 +122 +140 +69 +46 +287 +69 +268 +58 +134 +330 +172 +291 +175 +63 +184 +329 +30 +337 +229 +274 +130 +95 +255 +331 +24 +325 +228 diff --git a/labs/memory_debugging/middle.txt b/labs/memory_debugging/middle.txt new file mode 100644 index 0000000..19a5cf0 --- /dev/null +++ b/labs/memory_debugging/middle.txt @@ -0,0 +1,10 @@ +89 +34 +13 +5 +2 +1 +3 +8 +21 +55 diff --git a/labs/memory_debugging/protein.cpp b/labs/memory_debugging/protein.cpp new file mode 100644 index 0000000..fb179f8 --- /dev/null +++ b/labs/memory_debugging/protein.cpp @@ -0,0 +1,11 @@ +int tofu = 3; +int chicken = 2; +double** fish = new double*[tofu]; +for (int beef = 0; beef < tofu; beef++) { + fish[beef] = new double[chicken]; +} +fish[0][0] = 1.41421; +fish[0][1] = 1.61803; +fish[1][0] = 2.71828; +fish[1][1] = 3.14159; +fish[2][0] = 6.02214; diff --git a/labs/memory_debugging/veggies.cpp b/labs/memory_debugging/veggies.cpp new file mode 100644 index 0000000..ad75f22 --- /dev/null +++ b/labs/memory_debugging/veggies.cpp @@ -0,0 +1,10 @@ +char*** carrot; +char** broccoli; +char* tomato; +char radish = 'q'; +tomato = new char; +*tomato = 'z'; +broccoli = new char*; +*broccoli = tomato; +carrot = new char**; +*carrot = broccoli; diff --git a/labs/vectors/README.md b/labs/vectors/README.md new file mode 100644 index 0000000..ed173b7 --- /dev/null +++ b/labs/vectors/README.md @@ -0,0 +1,218 @@ +# Lab 5 — Vec Implementation + +## Checkpoint 1 +*estimate: 20 minutes* + +- Team up with one student in your lab section. MEET SOMEONE NEW! You may not work +with someone who was on your team for Lab 4. Ask a TA or mentor to help you find a partner. +If the number of students in the room is not even, the graduate TA will approve a single team with 3 +members. +- Introduce yourself to your teammate. Ask them to share something about themselves (e.g. hobbies, +sports, favorite music, etc.) Learn something new about your teammate (even if you already know +them). +- There are two sets of functions below. Each student in your team takes one set and working on it. + +For each function below, assign different letters to each of the data sizes that at first glance might have impact +on the running time of the function. Be sure to consider integer value, size of vector, and length of string. +Then give the big O notation of the function in terms of those variables. + +### Student 1 + +```cpp +int foobar (const std::vector &a, int b) { + int answer = 0; + for (int i = 0; i < a.size(); i+=b) { + answer++; + } + return answer; +} +``` + +```cpp +void foo2 (const std::vector &a, std::string &b) { + b.clear(); + for (int i = 0; i < a.size(); i++) + { + if (a[i] > 0) + b.push_back('+'); + else + b.push_back('-'); + } +} +``` + +```cpp +std::vector foo3 (const std::vector &a, const std::string &b) { + return std::vector(b.size(),a.size()); +} +``` + +```cpp +int foo3 (const std::vector &a, const std::string& b) { + int ret = 0; + for (int i=0; i foo4 (const std::vector &a) { + std::vector answer = a; + for (int i = 0; i < a.size(); i++) { + if(a[i] < (a[a.size()-1]*a[a.size()-1])){ + answer.erase(answer.end()-1); + } + } + return answer; +} +``` + +```cpp +std::vector foo5 (const std::vector &a, int b) { + std::vector ret; + for(int i=0; i &a, int b) { + int answer = 0; + for (int i = 0; i < a.size(); i+=b) { + answer++; + } + return answer; +} +``` + +```cpp +std::vector bar2 (const std::vector &a) { + std::vector answer; + for (int i = 0; i < a.size(); i++) { + answer.push_back(a[i].size()); + } + return answer; +} +``` + +```cpp +std::vector bar3 (const std::vector &a) { + std::vector answer; + for (int i = 0; i < a.size(); i++) { + answer.push_back(std::string(a[i],'+')); + } + return answer; +} +``` + +```cpp +void bar3 (std::vector &a, const std::string &b) { + for (int i = 0; i < a.size(); i++) { + a[i] = b; + } +} +``` + +```cpp +std::vector bar4 (const std::vector &a) { + std::vector answer; + if(!a.empty()){ + for (int i = 0; i < std::min(a[0].size(), a.size()); i++) { + answer.insert(answer.begin(),a[i].size()); + } + } + return answer; +} +``` + +```cpp +void bar5 (std::vector &a) { + for (int i = 0; i < a.size(); i++){ + if (a[i] > 0){ + a.erase(a.begin() + i); + i--; + } + } +} +``` + +When you finish, discuss these problems with your teammate. If your teammate hasn’t finished, please help +them (but without just doing the problems for them). + +Once you are both finished, type these examples into your C++ editor, add print statements, and confirm your answers are correct. What print statements will be most helpful? In your terminal, instead of running just + +```console +./a.out +``` + +try running + +```console +time ./a.out +``` + +and reading the real time, which is how long your program took to run. How does this change as you increase or decrease each of the data size variables you identified above? + +**To complete this checkpoint**, as a team, present your work to a TA/mentor. + +## Checkpoint 2 +*estimate: 30-40 minutes* + +Write a templated non-member function named remove_matching_elements that takes in two arguments, +a vector of type Vec<T> and an element of type T, and returns the number of elements that matched the +argument and were successfully removed from the vector. The order of the other elements should stay +the same. For example, if v, a Vec<int> object contains 6 elements: 11 22 33 11 55 22 and you call +remove_matching_elements(v,11), that call should return 2, and v should now contain: 22 33 55 22. +You should not create a new vector in your function. +Add several test cases to test_vec.cpp to show that the function works as expected. What is the order +notation of your solution in terms of n the size of the vector, and e the number of occurences of the input +element in the vector? + +*Note*: when you are in a non-member function, and you want to use the iterator, which is a member type of the Vec<T> class, you have to use the *typename* keyword before the Vec<T>:: scope. For example, if you want to define an iterator named *itr*, you can do it like this: + +```console +typename Vec::iterator itr +``` + +without this *typename* keyword, if you define the iterator *itr* like this: + +```console +Vec::iterator itr +``` + +you will get a compiler error saying: + +```console +error: need ‘typename’ before ‘Vec::iterator’ because ‘Vec’ is a dependent scope +``` + +And the reason that this keyword *typename* is needed, is because without it, the compiler would think that Vec<T>::iterator is a member variable of the Vec<T> class, but this *typename* explicitly tells the compiler that Vec<T>::iterator is a type, rather than a member variable. + +**To complete this checkpoint**, show a TA your debugged solution for remove_matching_elements and +be prepared to discuss the order notation of the function. + +## Checkpoint 3 +*estimate: 30 minutes* + +Add a print member function to Vec to aid in debugging. (Note, neither remove_matching_elements nor +print are part of the STL standard for vector). You should print the current information stored in the +variables capacity, m_size, and m_data. Use the print function to confirm your remove_matching_elements +function is debugged. Also, write a test case that calls push_back many, many times (hint, use a for loop!) +and observe how infrequently re-allocation of the m_data array is necessary. +To verify your code does not contain memory errors or memory leaks, use Valgrind and/or Dr. Memory on +your local machine – see instructions on the course webpage: Memory Debugging. Also, submit your code +to the homework server (in the practice space for lab 4), which is configured to run the memory debuggers +for this exercise. To verify that you understand the output from Valgrind and/or Dr. Memory, temporarily +add a simple bug into your implementation to cause a memory error or memory leak. + +**To complete this checkpoint**, show a TA your tested & debugged program. Be prepared to demo and +discuss the Valgrind and/or Dr. Memory output: with and without memory errors and memory leaks AND +on your local machine and on the homework server. diff --git a/labs/vectors/test_vec.cpp b/labs/vectors/test_vec.cpp new file mode 100644 index 0000000..762f0ed --- /dev/null +++ b/labs/vectors/test_vec.cpp @@ -0,0 +1,97 @@ +#include +#include +using namespace std; + +#include "vec.h" + +int main() { + + // --------------------------------------------------- + // initialize v1 with 10 values... the multiples of 5 + Vec v1( 10, 0 ); + int i; + for ( i = 0; i < v1.size(); i++) { + v1[i] = 5 * i; + } + cout << "v1.size() = " << v1.size() << ". Should be 10.\n"; + cout << "Contents of v1 (multiples of 5):"; + for ( i = 0; i v2( v1 ); + v2[ 9 ] = v2[ 0 ]; + v2[ 8 ] = v2[ 1 ]; + v2[ 7 ] = v2[ 2 ]; + v2[ 6 ] = v2[ 3 ]; + v2[ 5 ] = v2[ 4 ]; + cout << "Contents of v1 (still multiples of 5):"; + for ( i = 0; i v3; + v3 = v2; + v3.clear(); + cout << "\nAfter copying v2 to v3 and clearing v3, v2.size() = " + << v2.size() << " and v3.size() = " << v3.size() << endl; + cout << "Contents of v2 (should be unchanged):"; + for ( i = 0; i z; + for ( i = 0; i<5; ++i ) + z.push_back( sqrt( double(10*(i+1)) )); + cout << "Contents of vector z: "; + for (int j = 0; j < z.size(); j++ ) + cout << " " << z[j]; + cout << endl; + + + + // ADD MORE TEST CASES HERE + + + return 0; +} diff --git a/labs/vectors/vec.h b/labs/vectors/vec.h new file mode 100644 index 0000000..255d0aa --- /dev/null +++ b/labs/vectors/vec.h @@ -0,0 +1,141 @@ +#ifndef Vec_h_ +#define Vec_h_ +// Simple implementation of the vector class, revised from Koenig and Moo. This +// class is implemented using a dynamically allocated array (of templated type T). +// We ensure that that m_size is always <= capacity and when a push_back or resize +// call would violate this condition, the data is copied to a larger array. + +template class Vec { + +public: + // TYPEDEFS + typedef T* iterator; + typedef const T* const_iterator; + + // CONSTRUCTORS, ASSIGNMNENT OPERATOR, & DESTRUCTOR + Vec() { this->create(); } + Vec(int n, const T& t = T()) { this->create(n, t); } + Vec(const Vec& v) { copy(v); } + Vec& operator=(const Vec& v); + ~Vec() { delete [] m_data; } + + // MEMBER FUNCTIONS AND OTHER OPERATORS + T& operator[] (int i) { return m_data[i]; } + const T& operator[] (int i) const { return m_data[i]; } + void push_back(const T& t); + iterator erase(iterator p); + void resize(int n, const T& fill_in_value = T()); + void clear() { delete [] m_data; create(); } + bool empty() const { return m_size == 0; } + int size() const { return m_size; } + + // ITERATOR OPERATIONS + iterator begin() { return m_data; } + const_iterator begin() const { return m_data; } + iterator end() { return m_data + m_size; } + const_iterator end() const { return m_data + m_size; } + +private: + // PRIVATE MEMBER FUNCTIONS + void create(); + void create(int n, const T& val); + void copy(const Vec& v); + + // REPRESENTATION + T* m_data; // Pointer to first location in the allocated array + int m_size; // Number of elements stored in the vector + int capacity; // Number of array locations allocated, m_size <= capacity +}; + +// Create an empty vector (null pointers everywhere). +template void Vec::create() { + m_data = NULL; + m_size = capacity = 0; // No memory allocated yet +} + +// Create a vector with size n, each location having the given value +template void Vec::create(int n, const T& val) { + m_data = new T[n]; + m_size = capacity = n; + for (T* p = m_data; p != m_data + m_size; ++p) + *p = val; +} + +// Assign one vector to another, avoiding duplicate copying. +template Vec& Vec::operator=(const Vec& v) { + if (this != &v) { + delete [] m_data; + this -> copy(v); + } + return *this; +} + +// Create the vector as a copy of the given vector. +template void Vec::copy(const Vec& v) { + this->capacity = v.capacity; + this->m_size = v.m_size; + this->m_data = new T[this->capacity]; + + // Copy the data + for (int i = 0; i < this->m_size; ++i) + this -> m_data[ i ] = v.m_data[ i ]; +} + +// Add an element to the end, resize if necesssary. +template void Vec::push_back(const T& val) { + if (m_size == capacity) { + // Allocate a larger array, and copy the old values + + // Calculate the new allocation. Make sure it is at least one. + capacity *= 2; + if (capacity < 1) capacity = 1; + + // Allocate and copy the old array + T* new_data = new T[capacity]; + for (int i=0; i typename Vec::iterator Vec::erase(iterator p) { + // remember iterator and T* are equivalent + for (iterator q = p; q < m_data+m_size-1; ++q) + *q = *(q+1); + m_size --; + return p; +} + +// If n is less than or equal to the current size, just change the size. If n is +// greater than the current size, the new slots must be filled in with the given value. +// Re-allocation should occur only if necessary. push_back should not be used. +template void Vec::resize(int n, const T& fill_in_value) { + if (n <= m_size) + m_size = n; + else { + // If necessary, allocate new space and copy the old values + if (n > capacity) { + capacity = n; + T* new_data = new T[capacity]; + for (int i=0; i