Answered step by step
Verified Expert Solution
Question
1 Approved Answer
1 Objective This lab will give us practice with the C programming language. It will introduce us to various different aspects of C. Some of
1 Objective This lab will give us practice with the C programming language. It will introduce us to various different aspects of C. Some of the skills tested are: Explicit memory management, as required in C. Creating and manipulating pointer-based data structures and functions malloc and free. . Working with strings and functions strlen, strcpy, and strncpy. (Beware of the behavior of strncpy when truncating strings!) Enhancing the performance of key operations by storing redundant information in data structures. . Implementing robust code that operates correctly with invalid arguments, including NULL pointers. The lab involves implementing a queue, supporting both last-in, first-out (LIFO) and first- in-first-out (FIFO) queueing disciplines. The underlying data structure is a singly-linked list, enhanced to make some of the operations more e client. 2 Overview The file queue.h contains declarations of the following structures: /* Linked list element */ typedef struct list ele { char *value; struct list ele *next; } list_ele_t; /* Queue structure */ typedef struct { list_ele *head; /* First element in in the queue */ } queue_t; Queue List head List tail head NULL Additional Fields 63616200 6265 61 6400 63 61 62 00 b e a d a b Figure 1: Linked-list implementation of a queue. Each list element has a value field, pointing to an array of characters (C's representation of strings), and a next field pointing to the next list element. Characters are encoded according to the ASCII encoding (shown in hexadecimal.) These are combined to implement a queue of strings, as illustrated in Figure 1. The top- level representation of a queue is a structure of type queue_t. In the starter code, this structure contains only a single field head, but you will want to add other fields. The queue contents are represented as a singly-linked list, with each element represented by a structure of type list_ele_t, having fields value and next, storing a pointer to a string and a pointer to the next list element, respectively. The final list element has its next pointer set to NULL. You may add other fields to the structure list_ele, although you need not do so. Recall that a string is represented in C as an array of values of type char. In most machines, data type char is represented as a single byte. To store a string of length 1, the array has 1 + 1 elements, with the first / storing the codes (typically ASCIII format) for the characters and the final one being set to 0. The value field of the list element is a pointer to the array of characters. The figure indicates the representation of the list ["cab", "bead", "cab"), with characters a-e represented in hexadecimal as ASCII codes 61-65. Observe how the two instances of the string "cab" are represented by separate arrays. Each list element should have a eparate copy of its string. In our C code, a queue is a pointer of type queue_t*. We distinguish two special cases: a NULL queue is one for which the pointer has value NULL. An empty queue is one pointing to a valid structure, but the head field has value NULL. Your code will need to deal properly with both of these cases, as well as queues containing one or more elements. 3 Programming Task Your task is to modify the code in the files queue.h and queue.c to fully implement the following functions. queue_new: Create a new, empty queue. queue_free: Free all storage used by a queue. queue_insert_head: Attempt to insert a new element at the head of the queue (LIFO discipline) queue_insert_tail: Attempt to insert a new element at the tail of the queue (FIFO discipline) queue_remove_head: Attempt to remove the element at the head of the queue. queue_size: Compute the number of elements in the queue. queue_reverse: Reorder the list so that the queue elements are reversed in order. This function should not allocate or free any list elements (either directly or via calls to other functions that allocate or free list elements.) Instead, it should rearrange the existing elements. More details can be found in the comments in these two files, including how to handle invalid operations (e.g., removing from an empty or NULL queue), and what side effects and return values the functions should have. For functions that provide strings as arguments, you must create and store a copy of the string by calling malloc to allocate space (remember to include space for the terminating character) and then copying from the source to the newly allocated space. When it comes time to free a list element, you must also free the space used by the string. You cannot assume any fixed upper bound on the length of a string and you must allocate space for each string based on its length. Note: malloc, calloc, and realloc are the only supported functions in this lab for memory allocation, any other functions that allocate memory on the heap may cause you to lose points. Two of the functions, queue_insert_tail and queue_size, will require some effort on your part to meet the required performance standards. Naive implementations would require O(n) steps for a queue with n elements. We require that your implementations operate in time 0(1), i.e., that the operation will require only a fixed number of steps, regardless of the queue size. You can do this by including other fields in the queue_t data structure and managing these values properly as list elements are inserted, removed and reversed. Please work on nding a solution better than the O(n2) solution for all of the functions. Your program will be tested on queues with over 1,000,000 elements. You will find that you cannot operate on such long lists using recursive functions, since that would require too much stack space. Instead, you need to use a loop to traverse the elements in a list. 4. let P(m, n) be "n is greater than or equal to m" where the domain (universe of discourse) is the set of nonnegative integers. 4 Testing You can compile your code using the command: linux> make If there are no errors, the compiler will generate a executable program qtest, providing a command interface with which we can create, modify, and exam queues. Documentation on the available commands can be found by starting the program and running the help command: linux> ./qtest cmd> help The following file (traces/trace-eg.cmd) illustrates an example command sequence, which you can type into the qtest program: # Demonstration of queue testing framework # Initial queue is NULL. show # Create empty queue new # Fill it with some values. First at the head. ih dolphin ih bear ih gerbil # Now at the tail it meerkat it bear # Reverse it reverse # See how long it is size # Delete queue. Goes back to a NULL queue. free # Exit program quit You can also run qtest on an entire trace file all at once, as follows: linux> ./qtest -f traces/trace-eg.cmd # An example trace. linux> ./qtest -f traces/trace-01-ops.cmd # The first real trace. If you try to test the starter code, you will see that it does not implement many of the operations properly. The traces directory contains 15 trace files, with names of the form trace-k- cat.txt, where k is the trace number, and cat specifies the category of properties being tested. Each trace consists of a sequence of commands, similar to those shown above. They test different aspects of the correctness, robustness, and performance of your program. We can use these traces and direct interactions with qtest to test and debug your program. 5 Evaluation Your program will be evaluated using the fteen traces described above. You will be given credit (either 6 or 7 points, depending on the trace) for each one that executes correctly, summing to a maximum score of 100. This will be your score for the assignment, the grading is completely automated. This lab will not be graded on style. The driver program driver.py runs qtest on the traces and computes the score. This is the same program that will be used to compute your score. You can invoke the driver directly with the command: linux> ./driver.py or with the command: linux> make test 6 Logistics Our lab materials are contained in a Linux archive file cprogramminglab- handout.tar, which you can upload from Mimir. After uploading the file, give the command: linux> tar xvf cprogramminglab-handout.tar This will create a directory called cprogramminglab-handout that contains a number of files. Consult file README for descriptions of the files. You will modify the files queue.h and queue.c. score that you receive. Using make to generate qtest also has the effect of generating a tar file, handin.tar. Upload this file (and only this file!). You may hand-in as often as you like until the due date. IMPORTANT: Do not upload files in other archive formats, such as those with extensions .zip,.gzip, or.tgz. 1 Objective This lab will give us practice with the C programming language. It will introduce us to various different aspects of C. Some of the skills tested are: Explicit memory management, as required in C. Creating and manipulating pointer-based data structures and functions malloc and free. . Working with strings and functions strlen, strcpy, and strncpy. (Beware of the behavior of strncpy when truncating strings!) Enhancing the performance of key operations by storing redundant information in data structures. . Implementing robust code that operates correctly with invalid arguments, including NULL pointers. The lab involves implementing a queue, supporting both last-in, first-out (LIFO) and first- in-first-out (FIFO) queueing disciplines. The underlying data structure is a singly-linked list, enhanced to make some of the operations more e client. 2 Overview The file queue.h contains declarations of the following structures: /* Linked list element */ typedef struct list ele { char *value; struct list ele *next; } list_ele_t; /* Queue structure */ typedef struct { list_ele *head; /* First element in in the queue */ } queue_t; Queue List head List tail head NULL Additional Fields 63616200 6265 61 6400 63 61 62 00 b e a d a b Figure 1: Linked-list implementation of a queue. Each list element has a value field, pointing to an array of characters (C's representation of strings), and a next field pointing to the next list element. Characters are encoded according to the ASCII encoding (shown in hexadecimal.) These are combined to implement a queue of strings, as illustrated in Figure 1. The top- level representation of a queue is a structure of type queue_t. In the starter code, this structure contains only a single field head, but you will want to add other fields. The queue contents are represented as a singly-linked list, with each element represented by a structure of type list_ele_t, having fields value and next, storing a pointer to a string and a pointer to the next list element, respectively. The final list element has its next pointer set to NULL. You may add other fields to the structure list_ele, although you need not do so. Recall that a string is represented in C as an array of values of type char. In most machines, data type char is represented as a single byte. To store a string of length 1, the array has 1 + 1 elements, with the first / storing the codes (typically ASCIII format) for the characters and the final one being set to 0. The value field of the list element is a pointer to the array of characters. The figure indicates the representation of the list ["cab", "bead", "cab"), with characters a-e represented in hexadecimal as ASCII codes 61-65. Observe how the two instances of the string "cab" are represented by separate arrays. Each list element should have a eparate copy of its string. In our C code, a queue is a pointer of type queue_t*. We distinguish two special cases: a NULL queue is one for which the pointer has value NULL. An empty queue is one pointing to a valid structure, but the head field has value NULL. Your code will need to deal properly with both of these cases, as well as queues containing one or more elements. 3 Programming Task Your task is to modify the code in the files queue.h and queue.c to fully implement the following functions. queue_new: Create a new, empty queue. queue_free: Free all storage used by a queue. queue_insert_head: Attempt to insert a new element at the head of the queue (LIFO discipline) queue_insert_tail: Attempt to insert a new element at the tail of the queue (FIFO discipline) queue_remove_head: Attempt to remove the element at the head of the queue. queue_size: Compute the number of elements in the queue. queue_reverse: Reorder the list so that the queue elements are reversed in order. This function should not allocate or free any list elements (either directly or via calls to other functions that allocate or free list elements.) Instead, it should rearrange the existing elements. More details can be found in the comments in these two files, including how to handle invalid operations (e.g., removing from an empty or NULL queue), and what side effects and return values the functions should have. For functions that provide strings as arguments, you must create and store a copy of the string by calling malloc to allocate space (remember to include space for the terminating character) and then copying from the source to the newly allocated space. When it comes time to free a list element, you must also free the space used by the string. You cannot assume any fixed upper bound on the length of a string and you must allocate space for each string based on its length. Note: malloc, calloc, and realloc are the only supported functions in this lab for memory allocation, any other functions that allocate memory on the heap may cause you to lose points. Two of the functions, queue_insert_tail and queue_size, will require some effort on your part to meet the required performance standards. Naive implementations would require O(n) steps for a queue with n elements. We require that your implementations operate in time 0(1), i.e., that the operation will require only a fixed number of steps, regardless of the queue size. You can do this by including other fields in the queue_t data structure and managing these values properly as list elements are inserted, removed and reversed. Please work on nding a solution better than the O(n2) solution for all of the functions. Your program will be tested on queues with over 1,000,000 elements. You will find that you cannot operate on such long lists using recursive functions, since that would require too much stack space. Instead, you need to use a loop to traverse the elements in a list. 4. let P(m, n) be "n is greater than or equal to m" where the domain (universe of discourse) is the set of nonnegative integers. 4 Testing You can compile your code using the command: linux> make If there are no errors, the compiler will generate a executable program qtest, providing a command interface with which we can create, modify, and exam queues. Documentation on the available commands can be found by starting the program and running the help command: linux> ./qtest cmd> help The following file (traces/trace-eg.cmd) illustrates an example command sequence, which you can type into the qtest program: # Demonstration of queue testing framework # Initial queue is NULL. show # Create empty queue new # Fill it with some values. First at the head. ih dolphin ih bear ih gerbil # Now at the tail it meerkat it bear # Reverse it reverse # See how long it is size # Delete queue. Goes back to a NULL queue. free # Exit program quit You can also run qtest on an entire trace file all at once, as follows: linux> ./qtest -f traces/trace-eg.cmd # An example trace. linux> ./qtest -f traces/trace-01-ops.cmd # The first real trace. If you try to test the starter code, you will see that it does not implement many of the operations properly. The traces directory contains 15 trace files, with names of the form trace-k- cat.txt, where k is the trace number, and cat specifies the category of properties being tested. Each trace consists of a sequence of commands, similar to those shown above. They test different aspects of the correctness, robustness, and performance of your program. We can use these traces and direct interactions with qtest to test and debug your program. 5 Evaluation Your program will be evaluated using the fteen traces described above. You will be given credit (either 6 or 7 points, depending on the trace) for each one that executes correctly, summing to a maximum score of 100. This will be your score for the assignment, the grading is completely automated. This lab will not be graded on style. The driver program driver.py runs qtest on the traces and computes the score. This is the same program that will be used to compute your score. You can invoke the driver directly with the command: linux> ./driver.py or with the command: linux> make test 6 Logistics Our lab materials are contained in a Linux archive file cprogramminglab- handout.tar, which you can upload from Mimir. After uploading the file, give the command: linux> tar xvf cprogramminglab-handout.tar This will create a directory called cprogramminglab-handout that contains a number of files. Consult file README for descriptions of the files. You will modify the files queue.h and queue.c. score that you receive. Using make to generate qtest also has the effect of generating a tar file, handin.tar. Upload this file (and only this file!). You may hand-in as often as you like until the due date. IMPORTANT: Do not upload files in other archive formats, such as those with extensions .zip,.gzip, or.tgz
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started