ECE013 Doubly-linked lists
Introduction
In this lab, you will deepen and solidify the concept of pointers through the concept of adoubly-linked list data structure. You will implement a library to create, modify, and delete linkedlists. You will also learn to allocate and deallocate space during runtime. Finally, you will useyour library to compare two algorithms for counting unique words in a list. This lab will becompleted on both the Nucleo development kit and Linux.
Reading
- K&R – Chapters 5, 6.7, 7.8.5, appendix B5Concepts
- Doubly-linked lists
- Memory allocation
- Sorting
- Pointers (including NULL)
- Algorithmic analysisRequired Files:
- LinkedList.ce spec and prototypes for the linked-list functions youill implement.
o BOARD.c/h – Standard hardware library for ECE013.stopwatch.c/h – Tools for benchmarking the execution time of your program.
o Lab06_main.c – Default Makefile target; contains the main() function that youwill use to benchmark the algorithms that you develop in sort.c.o GNUmakefile — Used for compiling this lab on Linux. Edit these files:o sort.c – This file contains some starter code for a demonstration of the testingfor the sorting algorithms along with a function to generate a word list for theorts to operate on.▪It is included as sort_template.cCreate these files:o
LinkedList.c – Implement the library described in LinkedList.h.o LinkedListTest.c – A test harness for your LinkedList library. Should include amain().
- Your program will implement the functions whose prototypes are provided inLinkedList.h. The functions all have appropriate documentation and describe therequired functionality.
- Within LinkedListTest.c you will:oCreate a test harness that tests your linked list functions and ensuresthat they return the correct values. As in previous labs, at least two testsare required per function.oOnce you are done testing the functionality of LinkedList.c, yowillneedto exclude LinkedListTest.c from the project.
- Within sort.c you will:oImplement two algorithms for sorting linked lists inside the functionsSelectionSort() and InsertionSort().You will use these with theprovided main() code to perform timing experiments that will(hopefully) demonstrate the usefulness of linked lists. You should notmodify either main() or CreateUnsortedList() in this file.
- Add inline comments to explain your code.
- Create a readme file and export it to PDF named README.pdf containing thefollowing items. Spelling and grammar count as part of your grade so you'll wantto proof-read this before submitting. This will follow the same rough outline as alabparagraphs with several sentences in each paragraph.oFirst you should list your name & the names of colleagues who you haveollaborated with. 1oReport the results of the timing experiment in sort.c. Which was faster,SelectionSort() or InsertionSort()? Explain why. Was this what youexpected? How does performance compare on Linux vs your Nucleo?In the next section you should provide a summary of the lab in your ownwords. Highlightwhatyou thought were the important aspects of thelab. If these differ from how the lab manual presents things, make anote of that.o
he following section should describe your approach to the lab. Whatwas your general approach to the lab? Did you read the manual first orwhat were your first steps? What went wrong as you worked through it?What worked well? How would you approach this lab differently if youwere to do it again? How did you work with other students in the classand what did you find helpful/unhelpful?oThe final section should describe the results of you implementing thelab. How did it end up finally? How many hours did you end up spendingon it? What did you like about it? What did you dislike? Was this aworthwhile lab? Do you have any suggestions for altering it to make itbetter? What were the hardest parts of it? Did the points distribution forthe grading seem appropriate? Did the lab manual cover the material inenough detail to start you off? Did examples or discussions during classhelp you understand this lab or would more teaching on the concepts inthis lab help?1NOTE: collaboration != copying. If you worked withsomeone else, be DETAILED in yourdescription of what you did together.●Make sure that your code triggers no errors or warnings when compiling as theywill result in a significant loss of points.
- Follow the style guidelines.
Doing this Lab on Linux: For this lab we will continue using the make system. The commands as before arebelow. For this to work the file ‘GNUmakefile’ needs to be in the same directory asthe rest of your files, it is only used when compiling on Linux.
- $ make
o This command will create the final executable named Lab06
- $ make LinkedListTest
o This command will compile your linked list test harness into the
Pointers Pointers are covered thoroughly in the required reading for this lab, so if you are having
trouble, refer back to chapter 5 of K&R. However, there is one concept about pointersthat is not directly addressed in the reading: null pointers. A null pointer is a pointer tnothing. These pointers must be handled as a special case if they are passed to afunction that expects non-null data pointers. This is one of the major sourcesof crashesin programs.
The main problem with null pointers arises from when you try to dereference them(assuming x is an int pointer and equal to NULL): *x = 6;The reason for whybecomesclear when you think about代写ECE013 Doubly-linked lists what memory location x points
- 0, or NULL, is an invalid memory location, and a "null pointer dereference" erroroccurs because there is no memory location to write to, so an error occurs. This is a"fatal" error, which means that the program has no way to handle it, so the only thing itcan do is crash! This is a common cause of Windows blue-screen-of-death errors (whenthis dereferencing happens in kernel space 2 ). The solution to this is to check for nullpointers before dereferencing. An especially important case for checking to see if apointer is null is after any call to malloc() or calloc(), which we will cover a little later.
Doubly-linked lists
In computer programs, much as in real life, keeping a list of things can be useful. Usuallythe number of items that will be in this list is known ahead of time and so in a computerprogram this list could be kept in a standard C array. There will be occasions, like when
processing user input, when the number of items to be stored in a list is not knownahead of time. This is a problem with C’s statically-allocated arrays. The commonsolution is to use another data type called a linked list.Linked lists are exactly what they sound like: a collection of objects that are all linked
together to form a single list. As each item is linked to at least one other item in the list
there is a set ordering to the list: from a “head” item at the start to a “tail” item at thend. Since these items are all connected it is easy to access any item from any other itemby just traversing or “walking” through the list.r this lab you will be implementing a doubly-linked list, the more useful sibling of thenked lists. A doubly-linked list is also straightforward: each item is linked to both thetem before it and after it. This allows for traversal of the list from any element to anyother element by walking along it, which makes using the list very easy.2Note that kernel space vs user space is a key concept in protected mode multitaskingoperating systemssuch as Linux and WinNT derivatives. Using protected mode, a misbehaving user program cannot (intheory) crash the entire system. The absence of protected mode is a key property of embedded systems.The items in the list you are implementing are stored as structs in C because they willbe storing a few different pieces ofdata. Specifically it holds a pointer to the previousListItem, which will be NULL if it is the head of the list; a pointer to the nextListItem, which will be NULL if it’s at the end of the list; and a pointer to any kind ofdata (NULL if there’s no data). The typedef and the name after the “}” let you refer tothe struct in a similar fashion to any other data type, by using the single name“ListItem” instead of the longer “struct ListItem”.
The definition of the ListItem struct in LinkedList.h:
typedef struct ListItem {
struct ListItem *previousItem;
struct ListItem *nextItem;
char *data;
} ListItem;Now that you understand the structure of a linked list we will introduce the variousoperations that can be performed upon a list. The standard operations are creating anew list, adding elements to a list, finding the head of a list, and removing elementsfrom a list. 3
Creating a new list: A new list is created by just making a single ListItem. As this ListItemis both the head and tail of the list there is no item before it or after it in the list.Adding to a list: Now that you have a list, how do you add more elements to it? With thearrays that you are familiar with, you need to know two things: the position to insert intoand the data that will be inserted. With linked lists it’s a little different because there’snever a “free spot” to insert a new item into. What is done instead is that the position of
the new list item is relative to an existing item, generally the item before it in the list. Soto insert an item into the list, that item is inserted after an existing item. If the list went3It is incredibly useful to your understanding to draw this out on a piece ofpaper or a white board. Makeboxes for each member of the struct, and use arrows to point to the next list element and the previousones (in other words, use arrows to show what the pointers point to). Go through all of the functions andmake sure you understand what you need to do. Once youunderstand it conceptually, coding it up is verysimple.A <-> B <-> C and you want to insert D after B then the list would become A <-> B <-> D<-> C. So that means that the previous item and next item pointers of both B and C willneed to change to accommodate the new item D.
Finding the head: The head of a list is a special item because it has no precedingelement (represented by a NULL pointer). Since all the elements in a list areconnected,
finding the head merely requires traversing the list until a list item is found with nopreceding element. A function that finds the head of the list has one odd scenario; see ifyou can figure out what it is.Removing an element: Removing an element from a list is the opposite of adding to it.Following the example above you’d go from a list like A <-> B <-> D <-> C to A <-> B <-> CThe pointers of B and C both need to be modified to account for theremoval of D.enerally the data that was stored within D is also desired after the removal of the iemand should be returned.
malloc(), calloc(), and free()
This lab also relies on the use of memory allocation using malloc() (and/or calloc())
and free(). These are discussed somewhat in chapter 5 of K&R. As they are standardlibrary functions they are documented thoroughly online or in the Linux man-pages.Refer to those resources to understand them.It should be emphasized here that after any call to malloc() or calloc() you shouldalways check for NULL pointers! Memory allocation relies on the heap, which PlatformIOspecifies as 8192 bytes (0x200) for our Nucleo development kit by default. This projectrequires at least a couple hundred bytes for malloc() and calloc() to work, so wecanproceed with the default heap size. 44he linker script used by PlatformIO (which defines heap size) is auto-generated for allupported chip architectures. You can find the location of the linker script for STM32architectures under the “~/.platformio/packages/tool-ldscripts-ststm32/” path onLinux/UNIX-like operating systems.Note that this makes it easy to test that your code is properly checking for NULL
pointers: if you set the heap to 0, ALL calls to malloc()/calloc() will fail; if your codedoesn't crash, it's working!
Sorting
Sorting is an incredibly important function in computer programming. While you may
not think it is used a lot, it is quite common within a program to have the need to sort a
series of numbers. Sorting is an entire field of study within computer science and so
there are a huge number of algorithms that do just that. In this lab, you will be focusingon two: Selection Sort is simple, intuitive, and easy to implement on a static array, but itis slow. Insertion Sort is usually significantly faster, but it relies on a data structure thatallows insertion.Selection sort partitions the list into two partitions. The first partition is sorted, while
the second partition is unsorted. At the start of the algorithm, the sorted partition iempty.With each iteration, the sorted partition grows by one element, and theunsorted partition shrinks, until there are no remaining unsorted items. This is achievedby finding the smallest element in the unsorted partition and moving it to thesorted
partition.Pseudo-code for selection sort is provided below. This pseudocode is written in a waythat makes it easy to apply to linkedLists, but note that in this case the pointers couldeasily be replaced with indexes to a static array. We use two pointers: “FU” stands for
“First Unsorted”, representing the first item in the unsorted portion of the list. “S”
stands for “Scan”, since it “scans” through the unsorted partition, looking for the
smallest item.
FU is pointer to first item
while FU is not tail:
S is pointer to FU’s nextItem
while S is in list:
if FU > S:
swap FU and S contents
advance S
advance FUThe outer for loop effectively tracks the right-most element of the sorted array filling up
the left portion of the array. This means that for each iteration of the outer-loop, the
inner-loop can perform many element swaps.
Below is an example of Selection Sort in action:
D
A
// S is at end of list, advance FU
// FU is at tail, return
Insertion Sort operates in a similar way to selection sort. Like Selection Sort, it partitions the list
into a sorted and unsorted portion, and uses a double-loop structure to move items from theunsorted portion into the sorted portion. Unlike its slower cousin, insertion sort leverages an
“insert” operation to reduce the average time spent in the inner loop. Rather than “scanning”
through the unsorted partition in search of the smallest element, it scans through the sorted
portion to find the best place to insert the next item. It has the advantage that this scan does
not need to cover the entire sorted partition, instead stopping as soon as it finds the appropriate
place to insert.
Pseudocode for an insertion sort algorithm is given below. We use three pointers: “FS” stands
or “First Sorted”, and represents the first item in the sorted partition of the array. “LU” stands
or “Last Unsorted,” and represents the last item in the unsorted partition. Again “S” stands fo“scan,” since its job is to scan through the sorted partition to find appropriate insertionpoint.FS = tail of listwhile FS is not head of list:LU = FS’s previous itemif LU < FS:FS = LUelse
You will write code for both of these algorithms inside of the SelectionSort() and InsertionSort()functions in sort.c.
Evaluating SelectionSort() and InsertionSort() in Lab06_main.c
Although we have stated that InsertionSort() is faster than SelectionSort(), we shouldtest that claim experimentally. To do this, we will use the “stopwatch” library [includedin this assignment’s directory] to start, stop, and report the differences between theirexecution times.To measure the time required to execute SelectionSort(), we need to track the systemme immedIately before and after SelectionSort(), and then run our code. To do thisusing the stopwatch.c library included with this lab:
- Stopwatch_Init() – This function sets up your system’s clock; only needs to becalled once.
- Stopwatch_StartBenchmark() – Starts or restarts the stopwatch timer.
- Stopwatch_StopBenchmark() – Stops the stopwatch timer and saves the resultsin its local scope.4. Stopwatch_PrintBenchmarkResults() – Displays information about the cycles
required/time past for the last benchmark run. NOTE: You will need to call this
function to see the results after running “Stopwatch_StopBenchmark()”!
Approaching this lab Like all labs for this class, you should first start with implementing the LinkedList library.Be sure to handle when malloc() returns NULL, NULL pointers as arguments to functions,and whether the function expects the head of the list or not.
- Implement LinkedListNew().Test this by writing code to create a new list of size 1. Manually inspect theresultant struct that is created using the Variables window in VSCode/PlatformIO to see that it is correct while running on your Nucleo.
- Implement LinkedListCreateAfter() and LinkedListCreateBefore().Test these by creating a list of multiple sizes greater than 1. Manually inspect theresultant list using the Variables window in VS Code/PlatformIO.
- Now that you can create lists of a multitude of sizes, implementLinkedListGetFirst(). This function will be helpful for implementing the otherfunctions.Test this function by creating a few different lists, storing the pointer to the headnode. Pass a non-head node to GetFirst() and see if it matches the memoryaddress of the head node.
- Implement LinkedListGetSize().Run it on the different size lists you created earlier and confirm that results areas expected.
- Implement LinkedListPrint() and LinkedListSwapData().These should be straight-forward to test.
- At this point you are now ready for implementing SelectionSort() andInsertionSort in sort.c. The debugger will be very useful here. You may find itconvenient to use a shorter list during testing.
- Once SelectionSort() and InsertionSort() are functional, use the stopwatch tool
and the debugger to measure the time required for each sort. Record theresults in README.pdf and explain them.
A note on using GNUmakefile
One beautiful perk of using Makefiles is easy cleanup. While in the previous lab, you had toremove every object (.o) file and executable individually, the GNUmakefile provided for you inthis lab assignment can take care of this mess for you. Simply type into your terminal:Unset$ make cleanThis is just one of many useful features that you can reap from having a well-built Makefile,especially when dealing with larger projects. We encourage you to read more about buildingMakefiles. 6
A note on testing with PlatformIO/VS Code While Makefiles (e.g. GNUmakefile) allow you to specify the target that you would like tocompile and run on your microcontroller, for the purposes of our course you will only be able tobuild a PlatformIO project with one “main()” function declared withinits scope. For example inthis lab assignment, you will need to move either your LinkedListTest.c or your Lab06_main.c to a directory outside of the “src/” directory of your project for it to compile. We suggest leaving anyfiles that you do not want to build in your “Lab06/” directory for safe-keeping, then swapping
them between there and the “Lab06/src/” directory depending on which main() function youwould like to run.
How your code will be tested
Like previous labs, your code will be tested using your code as a library where we willtest with different programs to exercise your library on both Nucleo and Linux. YourLinkedListTest.c program will also be run so that we can observe your testing output.6A thorough (but perhaps overwhelming) guide to writing Makefiles is the GNU Make manual. This guidecontains a comprehensive description of the features offered by Makefiles, and it is not terribly-written;however, it is very long. A more practical approach, as with much software development, is to read andanalyze other developers’ code when you encounter it (key word: “analyze”).

浙公网安备 33010602011771号